Сколько дополнительных технических байт в строке хранит postgresql

проверим опытным путем на postgresql-9.3. Создадим разные таблицы, заполним 1.000.000 строк в каждую, и поищем закономирености.

таблица колонки данные

Общий

размер

Байт

на строчку

полезный

размер

дополнительные

байты

empty_1 c1 integer NULL 28.155.904 28 0 28
empty_2 c1 integer .. c4 integer NULL .. NULL 28.155.904 28 0 28
empty_3 c1 text NULL 28.164.096 28 0 28
empty_4 c1 integer[] NULL 28.164.096 28 0 28
empty_5 c1 text[] NULL 28.164.096 28 0 28
empty_6 c1 integer[] .. c4 integer[] NULL .. NULL 28.180.480 28 0 28
             
data_1 c1 integer 1 36.249.600 36 4 32
data_2 c1 integer .. c4 integer 1 .. 1 44.285.952 44 4*4=16 28
data_3 c1 text '' 36.249.600 36 0+4 32
data_4 c1 integer[] ARRAY[1] 60.235.776 60 4+4=8 50=30+20
data_5 c1 text[] ARRAY[''] 60.235.776 60 0+4+4=8 50=30+50
data_6 c1 integer[] .. c4 integer[] ARRAY[1] .. ARRAY[1] 134.356.992 134 (4+4)*4=32 102=26+19*4
data_7 c1 text[] .. c4 text[] ARRAY[''] .. ARRAY[''] 134.356.992 134 (0+4+4)*4=32 102=26+19*4
             
much_data_1 c1 text aaaaaaaaaa 44.285.952 44 10+4 30
much_data_25 c1 integer[] ARRAY[1x4] 68.280.320 68 4*4+4=20 48=28+20
much_data_31 c1 text[] ARRAY['aaaaaaaaaa'] 68.272.128 68 10+4+4=18 50=30+20
much_data_35 c1 text[] ARRAY['aaaaaaaaaa'x4] 117.030.912 117 (10+4)*4+4=60 57=30+27
much_data_41 c1 text[] ARRAY['aaaaa'] 68.313.088 68 5+4+4=13 50=30+20
much_data_45 c1 text[] ARRAY['aaaaa'x4] 101.187.584 101 (5+4)*4+4=40 61=30+31

Пояснения к колонкам.

Полезный размер - это очевидный размер, который реально нужно сохранить для данных. Например, для строки (кроме ее самой) нужно хранить дополнительно ее размер - 4 байта, это указано в документации postgres. Для массива тоже нужно хранить его размер - 4 байта. Integer - просто 4 байта.

Дополнительные байты - это байты которые дополнительно тратятся на техническую информацию. С правой части от = указано мое предположение как они разделяются между строкой и массивом

Что тут интересного.

Пустая строка занимает минимум 28 байт, а в среднем 30 байт технической информации - это видно по таблицам empty_X.

Зная это можно объяснить дополнительные байты в таблицах с массивами. На массивы тоже тратится техническая информация, и довольно много - примерно 20 байт на каждую колонку с массивом (если колонка не содержит NULL).

Некоторые вещи объяснить непонятно как: таблицы much_data_31 и much_data_41 занимают одинаково место, видимо дело в каких нибудь страницах, на которые разбиваются все данные.

Выводы

Технические данные на каждую строку - примерно 30 байт.

Технические данные на каждый столбец с массивом - примерно 20 байт.

SQL

CREATE TABLE empty_1(c1 integer);
CREATE TABLE empty_2(c1 integer, c2 integer, c3 integer, c4 integer);
CREATE TABLE empty_3(c1 text);
CREATE TABLE empty_4(c1 integer[]);
CREATE TABLE empty_5(c1 text[]);
CREATE TABLE empty_6(c1 integer[],c2 integer[],c3 integer[],c4 integer[]);

INSERT INTO empty_1 (c1) SELECT NULL FROM generate_series(1,1000000);
INSERT INTO empty_2 (c1,c2,c3,c4) SELECT NULL,NULL,NULL,NULL FROM generate_series(1,1000000);
INSERT INTO empty_3 (c1) SELECT NULL FROM generate_series(1,1000000);
INSERT INTO empty_4 (c1) SELECT NULL FROM generate_series(1,1000000);
INSERT INTO empty_5 (c1) SELECT NULL FROM generate_series(1,1000000);
INSERT INTO empty_6 (c1,c2,c3,c4) SELECT NULL,NULL,NULL,NULL FROM generate_series(1,1000000);

CREATE TABLE data_1(c1 integer);
CREATE TABLE data_2(c1 integer, c2 integer, c3 integer, c4 integer);
CREATE TABLE data_3(c1 text);
CREATE TABLE data_4(c1 integer[]);
CREATE TABLE data_5(c1 text[]);
CREATE TABLE data_6(c1 integer[],c2 integer[],c3 integer[],c4 integer[]);
CREATE TABLE data_7(c1 text[],c2 text[],c3 text[],c4 text[]);

INSERT INTO data_1 (c1) SELECT 1 FROM generate_series(1,1000000);
INSERT INTO data_2 (c1,c2,c3,c4) SELECT 1,1,1,1 FROM generate_series(1,1000000);
INSERT INTO data_3 (c1) SELECT '' FROM generate_series(1,1000000);
INSERT INTO data_4 (c1) SELECT ARRAY[1] FROM generate_series(1,1000000);
INSERT INTO data_5 (c1) SELECT ARRAY[''] FROM generate_series(1,1000000);
INSERT INTO data_6 (c1,c2,c3,c4) SELECT ARRAY[1],ARRAY[1],ARRAY[1],ARRAY[1] FROM generate_series(1,1000000);
INSERT INTO data_7 (c1,c2,c3,c4) SELECT ARRAY[''],ARRAY[''],ARRAY[''],ARRAY[''] FROM generate_series(1,1000000);

CREATE TABLE much_data_1(c1 text);
CREATE TABLE much_data_25(c1 integer[]);
CREATE TABLE much_data_31(c1 text[]);
CREATE TABLE much_data_35(c1 text[]);
CREATE TABLE much_data_41(c1 text[]);
CREATE TABLE much_data_45(c1 text[]);

INSERT INTO much_data_1(c1) SELECT 'aaaaaaaaaa' FROM generate_series(1,1000000);
INSERT INTO much_data_25(c1) SELECT ARRAY[1,1,1,1] FROM generate_series(1,1000000);
INSERT INTO much_data_31(c1) SELECT ARRAY['aaaaaaaaaa'] FROM generate_series(1,1000000);
INSERT INTO much_data_35(c1) SELECT ARRAY['aaaaaaaaaa','aaaaaaaaaa','aaaaaaaaaa','aaaaaaaaaa'] FROM generate_series(1,1000000);
INSERT INTO much_data_41(c1) SELECT ARRAY['aaaaa'] FROM generate_series(1,1000000);
INSERT INTO much_data_45(c1) SELECT ARRAY['aaaaa','aaaaa','aaaaa','aaaaa'] FROM generate_series(1,1000000);

подсчет размера таблиц

SELECT c.oid, n.nspname AS table_schema, relname AS TABLE_NAME, c.reltuples AS row_estimate, x.total_b, x2.table_b, x.index_b, x.toast_b, x3.total, x3.table, x3.index, x3.toast, x4.per_row
FROM pg_class c
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
JOIN LATERAL (SELECT pg_total_relation_size(c.oid) as total_b, pg_indexes_size(c.oid) AS index_b, pg_total_relation_size(reltoastrelid) AS toast_b) x ON TRUE
JOIN LATERAL (SELECT x.total_b-x.index_b-COALESCE(x.toast_b,0) AS table_b) x2 ON TRUE
JOIN LATERAL (SELECT pg_size_pretty(x.total_b) AS total, pg_size_pretty(x.index_b) AS INDEX, pg_size_pretty(x.toast_b) AS toast, pg_size_pretty(x2.table_b) AS table) x3 ON TRUE
JOIN LATERAL (SELECT (x.total_b/1000000)::int as per_row) x4 ON TRUE
WHERE relkind = 'r' and nspname = 'public' ORDER BY table_name

 

Яндекс.Метрика