Подтвердить что ты не робот

Понимание размеров строк Postgres

Я получил большую ( > 100M строк) таблицу Postgres со структурой {integer, integer, integer, timestamp без часового пояса}. Я ожидал, что размер строки будет 3 * целое число + 1 * timestamp = 3 * 4 + 1 * 8 = 20 байт.

В действительности размер строки pg_relation_size(tbl) / count(*)= 52 байта. Почему?

(Нет удалений по таблице: pg_relation_size(tbl, 'fsm') ~ = 0)

4b9b3361

Ответ 1

Расчет размера строки намного сложнее.

Хранение обычно разбивается на страницы данных . Существует небольшая фиксированная накладная плата на странице, возможные остатки не достаточно большие, чтобы соответствовать другим кортежам, и, что более важно, мертвые строки или процент, первоначально зарезервированный с помощью параметра FILLFACTOR.

Что еще более важно, есть накладные расходы на строку (кортеж). HeapTupleHeader из 23 байтов и выравнивание дополнений. Начало заголовка кортежа, а также начало данных кортежа выравниваются с кратким значением MAXALIGN, который составляет 8 байтов на типичной 64-битной машине. Некоторые типы данных требуют согласования с следующим кратным 2, 4 или 8 байтов.

Указание руководства в системной таблице pg_tpye:

typalign - это выравнивание, требуемое при сохранении значения этого типа. Он применяется для хранения на диске, а также для большинства представлений значение внутри PostgreSQL. При сохранении нескольких значений последовательно, например, в представлении полной строки на диск, прокладка вставлена ​​перед базой данных этого типа, чтобы она начинается с указанной границы. Ссылка на выравнивание - это начало первой точки отсчета в последовательности.

Возможные значения:

  • c= char выравнивание, т.е. не требуется выравнивание.

  • s= short выравнивание (2 байта на большинстве машин).

  • i= int выравнивание (4 байта на большинстве машин).

  • d= double выравнивание (8 байтов на многих машинах, но далеко не все).

Ознакомьтесь с основными сведениями в руководстве здесь.

Ваш пример

Это приводит к 4 байтам заполнения после столбцов 3 integer, потому что для столбца timestamp требуется выравнивание double и ему нужно начинать с следующего кратного 8 байтов.

Итак, одна строка занимает:

   23   -- heaptupleheader
 +  1   -- padding or NULL bitmap
 + 12   -- 3 * integer (no alignment padding here)
 +  4   -- padding after 3rd integer
 +  8   -- timestamp
 +  0   -- no padding since tuple ends at multiple of MAXALIGN

Наконец, в заголовке страницы есть указатель ItemData (указатель на элемент) для каждого кортежа (как отмеченный @AH в комментарии), который занимает 4 байт:

 +  4   -- item pointer in page header
------
 = 52 bytes

Итак, мы приходим к наблюдаемым 52 байтам.

Расчет pg_relation_size(tbl) / count(*) является пессимистической оценкой. pg_relation_size(tbl) включает в себя раздувание (мертвые строки) и зарезервированное пространство FILLFACTOR, а также накладные расходы на каждую страницу данных и таблицу. (И мы даже не упоминали сжатие для длинных данных varlena в таблицы TOAST, так как здесь это не применимо.)

Вы можете установить дополнительный модуль pgstattuple и вызвать SELECT * FROM pgstattuple('tbl_name'); для получения дополнительной информации о размере таблицы и кортежа.

Связанный ответ:

Ответ 2

Каждая строка имеет связанные с ней метаданные. Правильная формула (при условии выравнивания наивности):

3 * 4 + 1 * 8 == your data
24 bytes == row overhead
total size per row: 23 + 20

Или примерно 53 байта. Я действительно писал postgresql-varint, чтобы помочь с этой проблемой в этом конкретном случае использования. Вы можете захотеть просмотреть аналогичный пост для получения дополнительной информации re: накладные расходы.