Pull to refresh

Comments 21

Если создать колонку с компрессией LZ4 и хранить массив, разве не будет меньше?

LZ4 появляется только с PG14, и только для вытесняемых в TOAST данных. Для относительно коротких данных, как в нашем случае, они не будут туда попадать.

А для вас правда размер хранимых данных является наиболее важным критерием? Диски вроде бы относительно дешевые в наши дни.

Что насчёт того как эти конструкции ведут себя при чтении? А какой паттерн чтений? Например, часто ли нужно прочитать это дополнительное поле?

Я просто веду к тому что денормализация это иногда действительно очень хорошая штука. Вот только делать это ориентируясь исключительно на размер таблицы… Ну такое.

Не "наиболее", но одним из достаточно существенных. И не столько сам объем хранимых данных, сколько пропускная способность системы хранения - есть разница, писать 10Kops или 5Kops.

То есть как в анекдоте: диск может быть дешевым, объемным и производительным - выберите любые 2 из 3.

Насчет паттерна чтений - зависит от профиля. У нас, например, в системе упомянутой в статье в последнем абзаце, пишется много больше, чем читается, в силу особенностей мониторинга.

А потом вам HR отдел спускает задачу типа «страничка, показывающая где участвовал X.XX» и уже оказыватся что и индексировать и искать надо.
Кстати, не стоит делать выводы по таблицам в 5Мб размерам. На таблицах в 500 может стать все противоположным. Ну, к примеру, на мелких таблицах индексы ощутимо больший процент места отъедают.

Все-таки, индекс отъедает большую долю в "узкой" таблице вроде варианта-1, а от абсолютного объема практически не зависит - что для 5MB, что для 500MB, доли будут отличаться на десятые процента разве что.

А насчет изменения требований к БД - справедливо. Но с опытом можно научиться предугадывать вероятность, возникнет ли в данном куске у бизнеса желание расширить функционал.

Индекс — логарифмическая(ну, близка к логарифмической) структура и он на больших таблицах относительно меньшего обьема.
Ну возьмите простой пример, дерево для таблицы из 3х и 5ти строчек. Или дерево из 100 и 1000 позиций, какое будет более сбалансированным? А сбалансированное меньше места занимает.

В принципе, вы можете это эксперементально проверить. Просто у меня возникает улыбка при виде «вот смотрите 5мб табличка меньше 15мб». Иногда делаешь вроде все, чтоб интуитивно уменьшить размер, а табличка из 100гб становится 130. Еще и скорость операций падает втрое. Приходится откатывать.

Так вот, если вы это поместите в таблицу — неважно возникнет ли у кого-то такое желание, решить его не представит никакой проблемы. Да и если вы его вынесете как вы делаете, все равно по факту будет lookup, поскольку поле большое. Я не уверен на 100% что сделает постгресс с описанными вами типами, но mysql и oracle гарантированно текст выложат в отдельное место. И у вас гарантированно будет lookup, НО после него еще и парсинг.

Вообще сама по себе метрика «таблица на диске занимает больше места» никого не волнует. Волнует, сколько при этом она занимает в памяти, есть ли выигрыш или проигрыш в типичных операциях. Иногда даже есть ли выигрыш в одной конкретной, которая realtime и bottleneck, ибо отчеты по большому счету тоже никого не волнуют.

Индекс — логарифмическая(ну, близка к логарифмической) структура и он на больших таблицах относительно меньшего обьема.

Индекс - это структура для доступа за O(logN), но его объем растет примерно как O(N) с сохранением доли:

TRUNCATE TABLE tblpk RESTART IDENTITY CASCADE;
INSERT INTO tblpk(id) SELECT generate_series(1, 1e4);
VACUUM (FULL, ANALYZE, VERBOSE) tblpk;

SELECT relname, pg_relation_size(oid) FROM pg_class WHERE relname LIKE 'tblpk%' AND relkind IN ('r', 'i');
-- 10K
tblpk      |    368640
tblpk_pkey |    245760 -- 66.7%
-- 100K
tblpk      |   3629056
tblpk_pkey |   2260992 -- 62.3%
-- 1M
tblpk      |  36249600
tblpk_pkey |  22487040 -- 62.0%
-- 10M
tblpk      | 362479616
tblpk_pkey | 224641024 -- 62.0%

Так вот, если вы это поместите в таблицу — неважно возникнет ли у кого-то такое желание, решить его не представит никакой проблемы.

Это не совсем так, или даже совсем не так. Классический пример - организация хранения атрибутов объектов в виде EAV - гибко и удобно для разработчика. А потом приходит бизнес с пожеланием сделать по этим данным фасетный поиск - и ой...

Волнует, сколько при этом она занимает в памяти, есть ли выигрыш или проигрыш в типичных операциях.

Если мы говорим о most-write, то вообще не интересно, сколько там будет "в памяти", если основная нагрузка (== "типичная операция") - это запись на диск.

А почему мы это говорим о most-write если стандартный патерн — 75/25?

Смотря для каких задач стандартный. Например, у нас есть база мониторинга, где мы применили подобный подход замены массивов на строки, и получили профит - там ближе к 10/90.

Но даже если рассмотреть достаточно большую, не умещающуюся целиком в память, базу с доминирующим чтением, то подобные оптимизации тоже могут быть оправданы.

Представим, что база работает в режиме append-only, и пишет те самые 25% - тогда именно они и оседают в pagecache операционки. Но пользователи-то хотят видеть в остальные 75% далеко не только свежезаписанные данные, но и архивные из недалекого прошлого. А в кэше-то их уже давно нет - они доступны только на диске. И мы снова возвращаемся к мысли, что для производительности дисковой подсистемы выгодно не только "писать как можно меньший объем", но и "читать как можно меньший объем".

Хороший аналитик может оправдать практически что угодно. Мне не совсем понятно какое это имеет отношение к теме вашей статьи.

Во первых, ваши данные вообще не хайлоад. Во вторых не совсем DBA, а скорее, только постгрес.
Ну и выводы из табличек размером в мегабайты, извините, не очень серйозно воспринимаются хоть кем-то кроме джунов.

Хороший DBA моделирует на табличке в мегабайт, а думает о терабайтах, хайлоаде и экономии.

не совсем DBA, а скорее, только постгрес

А разве я где-то предлагал переносить данное решение на другие СУБД? Наоборот, несколько раз в статье подчеркивается, что это решение для PostgreSQL.

Неужели оно должно быть обязательным для всех и всегда универсальным рецептом? Или от неуниверсальности оно стало хуже?

Мы некоторые связи many-to-many со справочниками, где есть основания полагать, что количество записей в справочнике, пакуем в битовые строки. Из последнего, исходная таблица 10 гигабайт, агрегировали связь со справочником в массив, получили таблицу в размером примерно с гигабайт. Агрегировали эту же связь в виде битовой строки, получили чуть меньше 20 мегабайт.

А что по скорости доступа, поиска ?

При малом количестве элементов в списке одной записи, это вопрос микросекунд на уровне погрешности измерений.

А если сохранить доп. данные в jsonb в основной таблице, gyn индекс для поиска. Насколько объемно и эффективно получится?

У нас по условиям задачи нет необходимости в поиске по значениям в списке.

Потому что когда такая задача возникает, надо учитывать слишком много дополнительных условий вроде "надо ли искать по нескольким значениям сразу? по И или по ИЛИ?", и это влияет на решение гораздо сильнее, чем схема хранения.

Спасибо за статью. Мне интересно что посоветуете в моем случае. С точки зрения хорошей архитектуры. И потенциально огромной таблицы. Правда сейчас это не хайлоад и там не так много записей. Есть таблица товаров и у товаров есть таблица ивентов. Ивенты: created, moderated, updated, sold. И вот для ивента moderated я хочу хранить дополнительно кто промодерировал, а для sold кому продали и цену продажи. Я пришел к тому что просто добавил колонки sold_to где пользователь кому продали и price цена за которую продали и moderated_by где пользователь который промодерировал. Но для большинства записей в таблице ивентов, такие как created или updated эти колонки заполнены нулами. Это нормально или как мне сделать лучше если по этим полям (sold_to, moderated_by, price) поиск производиться не будет и показывать их тоже надо будет в исключительных или редких случаях?

Если не подразумевается каких-то странных выборок, то должно быть достаточно структуры вроде (item_id, event_type::enum, ts::timestamp, user_id).

так а где хранить кому продали, цену продажи и кто промодерировал? в отдельной таблице?

кто модерировал - это user_id, а вот под цену придется еще поле добавить и свой тип события - ведь событий продажи может быть больше одного

Sign up to leave a comment.