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

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

На самом деле размер строки pg_relation_size(tbl) / count(*) = 52 байта. Зачем?

(Удаление не выполняется в отношении таблицы: pg_relation_size(tbl, 'fsm') ~ = 0)

2 ответа

Решение

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

Хранилище обычно разбивается на страницы данных по 8 Кб. Существует небольшая фиксированная нагрузка на страницу, возможные остатки недостаточно велики для размещения другого кортежа и, что более важно, мертвые строки или процент, первоначально зарезервированный с помощью 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'); Для получения дополнительной информации о таблице и размере кортежа.

Соответствующий ответ:

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

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

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

Другие вопросы по тегам