PostgreSQL — Мечтаем про String Interning

На этой неделе на работе мы часто обсуждали Enum-ы, а также их хранение в БД PostgreSQL-а. После этого мне захотелось порассуждать о том, как было бы здорово, если бы PostgreSQL поддерживал умное хранение строк, применяя оптимизацию String Interning (другие названия — Пул строк, String Compaction).

Итак, какие вообще есть варианты хранения Enum значений в реляционной базе данных. На самом деле, есть как минимум 3 способа хранения значений:

  1. Можно хранить текстовое представление Enum-значения. То есть, если у нас есть Enum, обозначающий цвета, например —

    то можно хранить в базе данных именно эти значения — строки blue, red, black. Плюсы — простая и однозначная сериализация и десериализация. Кроме того, смотря в непосредственно БД, сразу становится понятно, что тут за значение. Минусы — это дорого по объему занимаемой памяти.

  2. Можно хранить числовые коды Enum-значений. Почти во всех языках программирования, каждому значению Enum сопоставляются неявно числа — 0, 1, 2. Давайте хранить именно их. Плюсы — это дешево по памяти. Минусы — нужно писать более сложный код сериализации и десериализации. Кроме того, смотря в базу данных, не понятно, что тут за значения.
  3. Можно, если Ваша база данных поддерживает это, использовать для хранения Enum непосредственно объект базы данных — Enum. Например, в PostgreSQL это есть — https://www.postgresql.org/docs/current/static/datatype-enum.html. Плюсы — вы получаете контроль со стороны Базы данных за этим объектом. Это будет ещё одно ваше ограничение целостности. Минусы — огромная сложность добавления нового значения Enum. Кроме того, vendor lock на базу данных. В общем, так себе вариант.

А давайте вообще посмотрим, о каких объемах памяти идёт речь. Может быть, это оптимизация на спичках. Может быть, нужно просто хранить строки в случае Enum-значений, и не париться.

Создадим первую тестовую таблицу. Она будет содержать 1 миллион значений — по примерно 10% строк: category1, category2, category3, category4, category5, category6, category7, category8, category9. То есть, у нас миллион значений, где всего 10 уникальных значений Enum.

А теперь заполним эту таблицу тестовыми данными:

Получим статистику по этой таблице:

Итог. Таблица занимает 42 MB места. В среднем, одна строка занимает 14 байт.

Теперь давайте создадим вторую таблицу. Она будет абстолютно такая же, как и первая — только хранится будут SMALLINT значения, а не строки.

Заполним и эту таблицу миллионом чисел от 1 до 9 включительно (аналогично категориям из первого примера):

И получим опять аналогично статистику (запросы не копирую сюда — они аналогичные).

Результаты. Таблица занимает 35 MB места. В среднем, одна строка занимает 3 байта.

С первого взгляда может показаться, что разница — не существенная. Речь идёт примерно о 20% памяти. Однако тут есть несколько Но:

  • Мы использовали в примере Тип CHAR(10). В реальной жизни строки могут быть на много больше. Следовательно, и разница потребления памяти изменится.
  • Есть некоторые общие расходы на таблицу. Если у нас будет не 1 миллион записей, как в примере, а 100 миллионов записей, выигрышь по памяти будет ещё больше, в случае хранения чисел, а не строк.

На данный момент в PostgreSQL не реализована оптимизация String Interning. На самом деле, если немного подумать, то сразу становится понятно, что не всё так однозначно, и эта оптимизация может здорово наломать дров:

  • String Interning улучшит Хранение данных, но нанесет вред параметру CPU consumption. Ведь, хоть у нас и биективное отображение строк в числа, в некоторых участак Базы Данных придётся выполнять достаточно много преобразований, что может значительно ухудшить общую производительность БД.
  • Наивная реализация String Interning может сломать некоторые свойства некоторых Индексов. Например, если взять дефолтный B-tree индекс, который обладает свойством отсортированности данных, то в случае замены строк на числа, сортировка сломается.

Возможно, кто-нибудь со стороны и запилит модуль, в котором будет реализован String Interning. И тогда мы уже сможем проверить на реальных данных, есть ли польза от такой оптимизации.

Категории: Программирование