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