Сколько дополнительных технических байт в строке хранит 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
- Log in to post comments