varchar(n) 的开销是多少?

key*_*ess 19 postgresql varchar database-internals

我想从Postgres 文档中询问这个片段关于varchar(n)类型的含义:

短字符串(最多 126 个字节)的存储要求是 1 个字节加上实际字符串,其中包括字符情况下的空格填充。较长的字符串有 4 个字节的开销而不是 1 个字节。

假设我有一个varchar(255)字段。现在,以下声明:

  • 如果该字段包含 10 个字节的字符串,则开销为 1 个字节。因此该字符串将使用 11 个字节。
  • 如果该字段使用 140 个字节保存字符串,则开销为 4 个字节。因此该字符串将使用 144 个字节。

上面的那些说法是真的吗?这里有人理解文档相同的方式,我不过这里有人指出的开销总是4个字节在这里

Erw*_*ter 22

不出所料,手册是正确的。但还有更多。

一方面,磁盘上的大小(在任何表中,即使实际上没有存储在磁盘上)可能与内存中的大小不同。在磁盘上,varchar最多 126 个字节的短值的开销减少到手册中所述的1 个字节。但是内存中的开销总是 4 个字节(一旦提取了单个值)。

text, varchar,varchar(n)char(n)-也是如此只是它char(n)被空白填充到n字符中,并且您通常不想使用它。它的有效大小在多字节编码中仍然可以变化,因为n表示最大字符数,而不是字节数:

字符串最长为n字符(不是字节)。

都是varlena内部使用。
"char"(带双引号)是一种不同的生物,总是占据一个字节。
无类型字符串文字( 'foo') 具有单字节开销。不要与类型化值混淆!

用 测试pg_column_size()

CREATE TEMP TABLE t (id int, v_small varchar, v_big varchar);
INSERT INTO t VALUES (1, 'foo', '12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890');

SELECT pg_column_size(id)        AS id
     , pg_column_size(v_small)   AS v_small
     , pg_column_size(v_big)     AS v_big
     , pg_column_size(t)         AS t
FROM   t
UNION ALL  -- 2nd row measuring values in RAM
SELECT pg_column_size(1)
     , pg_column_size('foo'::varchar)
     , pg_column_size('12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890'::varchar)
     , pg_column_size(ROW(1, 'foo'::varchar, '12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890'::varchar));

 id | v_small | v_big |  t
----+---------+-------+-----
  4 |       4 |   144 | 176
  4 |       7 |   144 | 176
Run Code Online (Sandbox Code Playgroud)

如你看到的:

  • 3 字节字符串 'foo'在磁盘上占用4 个字节,在 RAM 中占用7 个字节(因此 1 个字节与 4 个字节的开销)。
  • 140 字节的字符串 '123...' 在磁盘和 RAM 中都占用 144 个字节(因此总是 4 个字节的开销)。
  • 存储integer没有开销(但它具有可以施加填充的对齐要求)。
  • 该行的元组标题有 24 个字节的额外开销(加上页标题中的项目标识符每个元组额外的 4 个字节)。
  • 最后但并非最不重要的一点:small 的开销varchar仍然只有 1 个字节,而它还没有从行中提取 - 从行大小可以看出。(这就是为什么有时选择整行会快一点的原因。)

有关的: