Расчет и экономия места в PostgreSQL - PullRequest
55 голосов
/ 03 июня 2010

У меня есть таблица в pg, вот так:

CREATE TABLE t (
    a BIGSERIAL NOT NULL,               -- 8 b
    b SMALLINT,                         -- 2 b
    c SMALLINT,                         -- 2 b
    d REAL,                             -- 4 b
    e REAL,                             -- 4 b
    f REAL,                             -- 4 b
    g INTEGER,                          -- 4 b
    h REAL,                             -- 4 b
    i REAL,                             -- 4 b
    j SMALLINT,                         -- 2 b
    k INTEGER,                          -- 4 b
    l INTEGER,                          -- 4 b
    m REAL,                             -- 4 b
    CONSTRAINT a_pkey PRIMARY KEY (a)
);

Выше суммируется до 50 байтов на строку. Мой опыт показывает, что мне нужно еще 40–50% для системных издержек, даже без каких-либо созданных пользователем индексов. Итак, около 75 байт на строку. У меня будет много-много строк в таблице, потенциально больше 145 миллиардов строк, поэтому таблица будет загружать 13-14 терабайт. Какие уловки, если таковые имеются, я мог бы использовать для сжатия этой таблицы? Мои возможные идеи ниже ...

Преобразовать значения real в integer. Если они могут храниться как smallint, то это экономит 2 байта на поле.

Преобразование столбцов b .. m в массив. Мне не нужно искать по этим столбцам, но мне нужно иметь возможность возвращать значение одного столбца за раз. Итак, если мне нужен столбец g, я мог бы сделать что-то вроде

SELECT a, arr[5] FROM t;

Буду ли я экономить место с опцией массива? Будет ли штраф за скорость?

Есть еще идеи?

Ответы [ 2 ]

171 голосов
/ 15 сентября 2011

"Колонна тетриса"

На самом деле, вы можете сделать что-то , но это требует более глубокого понимания. Ключевое слово: отступ выравнивания . Каждый тип данных имеет особые требования к выравниванию .

Вы можете минимизировать пространство, потерянное для отступов между столбцами , упорядочив их. В следующем (крайнем) примере будет потрачено много физического дискового пространства:

CREATE TABLE t (
    e int2    -- 6 bytes of padding after int2
  , a int8
  , f int2    -- 6 bytes of padding after int2
  , b int8
  , g int2    -- 6 bytes of padding after int2
  , c int8
  , h int2    -- 6 bytes of padding after int2
  , d int8)

Чтобы сохранить 24 байта на строку, используйте вместо:

CREATE TABLE t (
    a int8
  , b int8
  , c int8
  , d int8
  , e int2
  , f int2
  , g int2
  , h int2)   -- 4 int2 occupy 8 byte (MAXALIGN), no padding at the end

SQL Fiddle.

Как правило, если сначала поставить 8-байтовые столбцы, а затем 4-байтовые, 2-байтовые и 1-байтовые столбцы, вы не ошибетесь.

boolean, uuid и некоторые другие типы не требуют выравнивания. text, varchar и другие типы "varlena" (переменной длины) номинально требуют выравнивания "int" (4 байта на большинстве машин). Но на самом деле нет выравнивания выравнивания в формате диска (в отличие от RAM). Я проверен во многих тестах. В конце концов я нашел объяснение в примечании в исходном коде:

Обратите внимание, что мы допускаем нарушение номинального выравнивания при хранении "упакованных" варен;

Обычно вы можете сэкономить пару байтов на строку, в лучшем случае проиграв "столбец тетриса" . В большинстве случаев это не нужно. Но с миллиардами строк это может означать пару гигабайт легко.

Фактический размер столбца / строки можно проверить с помощью функции pg_column_size().
Некоторые типы занимают больше места в оперативной памяти, чем на диске (сжатый или «упакованный» формат). Вы можете получить более высокие результаты для констант (в формате RAM), чем для столбцов таблицы, когда тестируете одно и то же значение (или строку значений и строку таблицы) с pg_column_size().

Наконец, некоторые типы могут быть сжатыми или "поджаренными" (сохранены вне строки) или и тем, и другим.

Накладные расходы на кортеж (строку)

4 байта в строке для указателя элемента - без учета вышеуказанных соображений.
И как минимум 24 байта (23 + заполнение) для заголовка кортежа. Руководство по разметке страницы базы данных:

Существует заголовок фиксированного размера (занимающий 23 байта на большинстве машин), за которым следует необязательное пустое растровое изображение, необязательное поле идентификатора объекта и пользовательские данные.

Для заполнения между заголовком и пользовательскими данными вам необходимо знать MAXALIGN на вашем сервере - обычно 8 байтов в 64-битной ОС (или 4 байта в 32-битной ОС). Если вы не уверены, проверьте pg_controldata.

Запустите следующее в вашем двоичном каталоге Postgres , чтобы получить окончательный ответ:

./pg_controldata /path/to/my/dbcluster

Руководство:

Фактические данные пользователя (столбцы строки) начинаются со смещения обозначено t_hoff, которое всегда должно быть кратным MAXALIGN расстояние до платформы.

Таким образом, вы обычно получаете оптимальный объем хранения, упаковывая данные в кратные 8 байт.

Ничего не получится в примере, который вы разместили . Это уже плотно упаковано. 2 байта заполнения после последнего int2, 4 байта в конце. Вы можете объединить заполнение до 6 байтов в конце, что ничего не изменит.

накладные расходы на страницу данных

Размер страницы данных обычно составляет 8 КБ. Некоторые издержки / раздувание на этом уровне тоже: остатки недостаточно велики для размещения другого кортежа и, что более важно, мертвые строки или процент, зарезервированный с параметром FILLFACTOR .

Существует несколько других факторов, влияющих на размер диска:

Типы массивов?

При использовании типа array , который вы оценивали, вы добавили бы 24 байта служебных данных для этого типа. Плюс, элементы массива занимают место как обычно. Нечего там приобретать.

11 голосов
/ 03 июня 2010

Я не вижу никакой выгоды (и чего-то, что можно потерять) при хранении нескольких числовых полей в массиве.

Размер каждого числового типа четко задокументирован, вы должны просто использовать тип наименьшего размера, совместимый с желаемым разрешением по диапазону; и это все, что вы можете сделать.

Я не думаю (но я не уверен), существует ли какое-либо требование выравнивания байтов для столбцов вдоль строки, в этом случае изменение порядка столбцов может изменить используемое пространство - но я не думаю, что поэтому.

Кстати, в каждой строке есть фиксированные накладные расходы, около 23 байта .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...