Каковы накладные расходы для varchar (n)?

15

Я хотел спросить значение этого фрагмента в документации Postgres относительно varchar(n)типа:

Требование к памяти для короткой строки (до 126 байт) составляет 1 байт плюс фактическая строка, которая включает пробел в случае символа. Более длинные строки имеют 4 байта служебной информации вместо 1.

Давайте предположим, что у меня есть varchar(255)поле. А теперь следующие высказывания:

  • Если это поле содержит строку из 10 байтов, тогда служебная информация составляет 1 байт. Таким образом, строка будет использовать 11 байтов.
  • Если поле содержит строку, используя 140 байтов, тогда издержки составляют 4 байта. Таким образом, строка будет использовать 144 байта.

Эти утверждения выше верны? Здесь кто-то понимает документ так же, как я, но здесь кто-то заявляет, что служебные данные всегда 4 байта здесь ?

нажатие клавиши
источник

Ответы:

19

Неудивительно, что руководство является правильным. Но это еще не все.

С одной стороны, размер на диске (в любой таблице , даже если он не хранится на диске) может отличаться от размера в памяти . На диске служебная информация для коротких 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

Как вы видете:

  • Трехбайтовая строка 'foo' занимает 4 байта на диске и 7 байтов в ОЗУ (таким образом, 1 байт против 4 байтов служебных данных).
  • 140-байтовая строка «123 ...» занимает 144 байта как на диске, так и в оперативной памяти (поэтому всегда 4 байта служебных данных).
  • Хранение integerне имеет накладных расходов (но оно имеет требования к выравниванию, которые могут накладывать отступы).
  • Строка имеет дополнительные служебные данные в 24 байта для заголовка кортежа (плюс дополнительные 4 байта на кортеж для указателя элемента в заголовке страницы).
  • И последнее по порядку, но не по значению: varcharразмер заголовка по- прежнему составляет всего 1 байт, пока он не извлечен из строки - как видно из размера строки. (Вот почему иногда бывает немного быстрее выделять целые строки.)

Связанный:

Эрвин Брандштеттер
источник
1
Это 1 байт все еще 1 байт в индексе?
17
1
@dtgq: индекс хранит данные как таблица, так что да.
Эрвин Брандштеттер