【问题标题】:FIxed storage sized ASCII data type?固定存储大小的 ASCII 数据类型?
【发布时间】:2020-02-27 20:07:23
【问题描述】:

我有一个需要存储 UN/LOCODE 值的数据库,我发现,使用标准数据类型,不可能以固定大小的形式存储这些值,这样我就可以有效地播放俄罗斯方块。

目前,我已经为它定义了一个域如下:

-- city: UN/LOCODE https://service.unece.org/trade/locode/Service/LocodeColumn.htm#LOCODE
CREATE DOMAIN t_locode AS VARCHAR
    CONSTRAINT t_locode_check CHECK (
        value IS NULL
        OR
        value ~ '^[A-Z]{2} [A-Z][A-Z0-9]{1,2}$'
);

...导致:

testing=# \dT+ t_locode 
List of data types
-[ RECORD 1 ]-----+---------
Schema            | public
Name              | t_locode
Internal name     | t_locode
Size              | var
Elements          | 
Owner             | postgres
Access privileges | 
Description       | 

我还尝试使用固定长度的字符类型定义而不是VARCHAR(例如CHAR(6)),但这根本没有改变,我怀疑这是因为数据库本身设置为UTF- 8,顾名思义就是变长字符编码。

为了进一步解决该问题,该域随后用于其他用户数据类型。

充其量,我想定义一种数据类型,它可以使用最多 8 个字节的存储空间来存储这些值,以与双精度对齐。

有人知道解决办法吗?

【问题讨论】:

  • 您的域名到底有什么问题? “列俄罗斯方块”是什么意思?
  • 我的问题是它在存储上不是固定大小的,而是可变的,因此最终在扩展存储中。但是,由于我有某些表使用带有“NOT NULL”的域,出于性能和表大小的原因,我希望将它们包含在普通存储中(一些表将在 10-30 列有数十亿行) .
  • “列俄罗斯方块”我指的是以下内容:2ndquadrant.com/en/blog/on-rocks-and-sand
  • "在扩展存储中结束" - 如果它们短于 ~2k,则不会
  • @a_horse_with_no_name 你有什么参考资料可以让我了解更多吗?

标签: postgresql


【解决方案1】:

字符串数据类型在 PostgreSQL 中总是可变宽度数据类型,无论你使用textcharacter varying 还是character

我认为您正在尝试优化不需要优化的东西。这种尝试往往弊大于利。

如果您的字符串总是 6 个 ASCII 字符长,它们将占用 7 个字节的存储空间:

CREATE TABLE x(id bigint, t text);

INSERT INTO x VALUES (1, '      '), (2, '000000');

CREATE EXTENSION pageinspect;

SELECT t_ctid, t_attrs FROM heap_page_item_attrs(get_raw_page('x', 0), 'x');

 t_ctid |                   t_attrs                   
--------+---------------------------------------------
 (0,1)  | {"\\x0100000000000000","\\x0f202020202020"}
 (0,2)  | {"\\x0200000000000000","\\x0f303030303030"}
(2 rows)

这是一个 little-endian 机器,您可以从 bigint 列中看到。

您会注意到text 值每个仅占用 7 个字节。这是由TOAST 造成的,它是你的朋友,而不是你的敌人:

TOAST 篡夺了 varlena 长度字的两位(大端机器上的高位,小端机器上的低位),从而限制了可 TOAST 数据的任何值的逻辑大小类型为 1 GB(230 - 1 个字节)。 [...]当设置最高位或最低位时,该值只有一个单字节头而不是正常的四字节头,该字节的其余位给出总数据大小(包括长度字节)以字节为单位。

0x0F 是二进制的00001111:最右边的1 表示我们只有一个单字节的标头,剩下的0000111(十进制7)是数据的长度,包括标头。

由于您的值只有 7 个字节长,它们将与 double precision 很好地对齐,只丢失一个填充字节。

如果您想通过避免填充字节来进行微优化,请将所有字符串列彼此相邻放在表定义的末尾。另一方面,考虑到 PostgreSQL 必须遍历前 9 列才能到达第 10 列,因此将经常使用的列放在首位会有性能优势。

但我不会太担心这些问题:如果您发现需要更大的数字,则使用 integer 而不是 bigint 节省 4 个字节可能会导致大问题,并且列的放置无关紧要在性能方面,与良好的数据模型、良好的查询和正确的索引相比。

【讨论】:

  • 非常感谢您提供这些见解以及使用本机工具验证正在发生的事情的示例。这对我会派上用场!
猜你喜欢
  • 2021-06-25
  • 2018-05-25
  • 1970-01-01
  • 2018-02-12
  • 2011-02-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-02
  • 2011-01-07
相关资源
最近更新 更多