Искусственный упрощённый пример, который +- эквивалентен моей задаче:
Снапшоты файловой системы, в виде 3х таблиц:
1) сами снапшоты
2) содержимое файлов (например: имя, хеш, размер)
3) ссылка между 1) и 2), собирающая нужные файлы в снапшот.
Проблема в том, что хоть записи в 3) весьма компактные, но файлов ОЧЕНЬ много. А вот изменений между снапшотами довольно мало.
Появилась идея, иметь некий базовый снапшот, и ещё два таблицы:
а) ссылки, которые надо удалить из базового снапшота
б) ссылки, которые надо добавить в базовый снапшот
(соотв. изменение файла может быть трактовано как удаление старого содержимого + добавление нового содержимого).
Т.е. накладыванием "патча" получаем нужную нам таблицу.
Вопросы:
— боюсь, при реализации "в лоб", с помощью двух таблиц, будет тормозить. Или нет ?
— может быть можно как-то абстрагировать всю кухню, например параметризованной вьюшкой ? Не будет ли проблем с индексами ?
— модет быть есть существующие практики, как подобное принято решать ? Ещё лучше если встроенным в БД механизмом.
Здравствуйте, IID, Вы писали:
IID> Искусственный упрощённый пример, который +- эквивалентен моей задаче:
IID> Снапшоты файловой системы, в виде 3х таблиц: IID> 1) сами снапшоты IID> 2) содержимое файлов (например: имя, хеш, размер) IID> 3) ссылка между 1) и 2), собирающая нужные файлы в снапшот.
IID> Проблема в том, что хоть записи в 3) весьма компактные, но файлов ОЧЕНЬ много. А вот изменений между снапшотами довольно мало.
Может как в git сделать ещё tree? Т.е. изменение файла поменяет только tree объекты до корня. tree можно создавать искуственно, как в git notes, например по префиксу хеша.
Иными словами, смоделируй хранилище git. Оно очень эффективно и заточено под твой сценарий — много файлов, много снапшотов, мало разницы между снапшотами.
IID>Искусственный упрощённый пример, который +- эквивалентен моей задаче:
IID>Т.е. накладыванием "патча" получаем нужную нам таблицу.
А "второе поколение" (производное состояние от измененного) как планируете делать? Патч к патчу или полностью новый снимок? Патч к патчу можно, но запросы будут сложнее (постгрес поддерживает рекурсивные запросы). Хотя можно начать "как-то" и потом уже пытаться совершенствовать. Статистику изменений ("размер дельты") может быть полезно собирать сразу.
IID>Вопросы: IID>- боюсь, при реализации "в лоб", с помощью двух таблиц, будет тормозить. Или нет ?
Зависит от множества факторов . В том числе от собранной статистики и запросов, которые выполняются. По идее у вас в запросах должен выходить JOIN с вашими двумя таблицами, который может выполняться как hash join. Еще нужно аккуратно с заменой. UNION ALL + EXCEPT может чуть добавить сложностей, но это можно и в обычные join перепиать (с подзапросом, наверное).
IID>- может быть можно как-то абстрагировать всю кухню, например параметризованной вьюшкой ? Не будет ли проблем с индексами ?
Нет параметризованных вью в постргресе. Есть обычные. Да, можно сделать через view и даже стоит так сделать. Если вдруг вы решите изменить схему хранения данных (добавить уровни, изменить структуру патча и т.п.), то изменится только view и код записи. А код чтения останется. Проблем с индексами быть не должно. Если я правильно помню, в postgresql вьюшки при построении класса запроса встраиваются в основной (т.е. по сути являюсят "подзапросом") и потом оптимизатор и планировщик работают с запросами, в которых доступ идет только к "реальным" хранилищам (таблицам и materialized view).
Кстати, а в клиентском коде можно выделить сервис для чтения/записи снапшотов? Можно ведь сделать один/два запроса на загрузку патча и применять его на клиенте. Да, два запроса к базе. Но вряд ли это так критично на начальном этапе. Да и на фоне загрузки снапшотов (вы же говорите, что они большие) один лишний запрос вряд ли будет заметен.
IID>- модет быть есть существующие практики, как подобное принято решать ? Ещё лучше если встроенным в БД механизмом.
Я бы, наверное, немного по-другому построил:
1) Снапшоты (возможно, с родителями)
2) "Содержимое" (хеш/размер/данные)
3) Связи из (снапшот, имя) в (содержимое). По первой паре — индекс.
4) Патчи в виде тройки (снапшот, имя, новое содержимое). Новое содержимое может быть null.
Не обязательно даже "патч" предоставлять в виде отдельной таблицы. Это может быть такая же запись в виде базового содержимого.
Как-то более интуитивно получаются запросы. Например, получить содержимое:
SELECT
${snapshot_id} AS snapshot_id,
COALESCE(parent_link.name, pt.name) AS name,
CASE
WHEN pt.name IS NOT NULL THEN pt.content_id
ELSE parent_link.content_id
END as content_id
FROM snapshots snap
JOIN links parent_link ON parent_link.snaphsot_id = snap.parent_id
FULL JOIN (SELECT * FROM links WHERE snapshot_id = ${snapshot_id}) pt ON pt.name = parent_link.name
WHERE snap.id = ${snapshot_id}
HAVING content_id IS NOT NULL
Я здесь условно считаю, что все данные и обновления в одной и той же таблице links хранятся.
В общем, пробуйте. Изолируйте по возможности клиентов от деталей реализации (сервис в коде, вью в базе) и экспериментируйте. Собирайте статистику. А там уже решите, что лучше. Может нужно будет поменять схему в базе. А может добавить кэш для "полных" снапшотов в клиенте и применять патчи уже в коде.
Здравствуйте, maxkar, Вы писали:
M>А "второе поколение" (производное состояние от измененного) как планируете делать? Патч к патчу или полностью новый снимок? Патч к патчу можно, но запросы будут сложнее (постгрес поддерживает рекурсивные запросы).
Никаких "патч-к-патчу". Только базовый снапшот и опциональные ADD/DEL патчи.
Максимум — после создания N полноценных снапшотов проводить их "ночную" оптимизацию. Искать самую выгодную базу/базы. И от неё/них делать патчи.
Типа keyframes в кодировщиках видео.
IID>>Вопросы: IID>>- боюсь, при реализации "в лоб", с помощью двух таблиц, будет тормозить. Или нет ? M>По идее у вас в запросах должен выходить JOIN с вашими двумя таблицами, который может выполняться как hash join. Еще нужно аккуратно с заменой. UNION ALL + EXCEPT может чуть добавить сложностей, но это можно и в обычные join перепиать (с подзапросом, наверное).
Я вот думал обойтись только EXCEPT + UNION
M>Кстати, а в клиентском коде можно выделить сервис для чтения/записи снапшотов? Можно ведь сделать один/два запроса на загрузку патча и применять его на клиенте. Да, два запроса к базе. Но вряд ли это так критично на начальном этапе. Да и на фоне загрузки снапшотов (вы же говорите, что они большие) один лишний запрос вряд ли будет заметен.
Клиентский код это Python-импортёр состояния в базу.
Ну может ещё периодически запускаемые оптимизатор, если встроенные средства, типа хранимок, окажутся слабее (сомневаюсь).
Вся аналитика по собранным данным должна строиться обычными SQL запросами. Например как много у нас дубликатов файлов, в снапшотах, собранных на разных машинах. И т.д.
IID>>- модет быть есть существующие практики, как подобное принято решать ? Ещё лучше если встроенным в БД механизмом. M>Я бы, наверное, немного по-другому построил: M>1) Снапшоты (возможно, с родителями) M>2) "Содержимое" (хеш/размер/данные) M>3) Связи из (снапшот, имя) в (содержимое). По первой паре — индекс.
Примерно так сейчас и есть.
M>4) Патчи в виде тройки (снапшот, имя, новое содержимое). Новое содержимое может быть null.
А почему не две таблицы ? Чтобы содержимое всегда не null для добавления, а для удаления вообще это поле убреть.
M>Не обязательно даже "патч" предоставлять в виде отдельной таблицы. Это может быть такая же запись в виде базоого содержимого.
Зато подзапросы не нужны.
"вычли" из базового снапшота все патчи удаления
"приклеили" в него все патчи добавления.
Здравствуйте, IID, Вы писали:
M>>По идее у вас в запросах должен выходить JOIN с вашими двумя таблицами, который может выполняться как hash join. Еще нужно аккуратно с заменой. UNION ALL + EXCEPT может чуть добавить сложностей, но это можно и в обычные join перепиать (с подзапросом, наверное). IID>Я вот думал обойтись только EXCEPT + UNION
Ну значит попробуйте только EXCEPT + UNION (или UNION ALL, он может быть чуть-чуть быстрее) . Я с EXCEPT/UNION практически не работал, не знаю, во что он внутри преобразуюется. Сделаете EXPLAIN, посмотрите, что получается. Может он там в JOIN и так преобразует.
IID>Вся аналитика по собранным данным должна строиться обычными SQL запросами.
Тогда вью, да. И смотреть на планы, которые получаются, в том числе и для результирующих аналитических запросов. Хранимые процедуры/функции тоже можно, но view вроде бы гораздо лучше поддается оптимизации в составе других запросов, чем хранимки (собственно, plsql-функции вообще не оптимизируются, про sql-функции не знаю).
IID>Зато подзапросы не нужны.
Согласен . Сделайте как проще и view с результатами (чтобы при изменениях не нужно было много запросов переделывать). А дальше уже на реальных данных и запросах смотреть, что получается.
похоже, но не то. Их фича — версионность всей базы, возможность делать бранчи и слияния.
Мне же нужно только версионность между N снапшотами одной таблицы. Эта таблица будет содержать несколько сотен параллельных "репозиториев". И нужно только создание и чтение. Но чтение очень быстрое.
Здравствуйте, IID, Вы писали:
IID>Мне же нужно только версионность между N снапшотами одной таблицы. Эта таблица будет содержать несколько сотен параллельных "репозиториев". И нужно только создание и чтение. Но чтение очень быстрое.
Тебе нужно быстрое чтение только последней версии? Или любого снапшота?
Подробнее опиши задачу. Желательно без упрощений и аналогий.
Здравствуйте, wildwind, Вы писали:
W>Тебе нужно быстрое чтение только последней версии? Или любого снапшота?
Любого снапшота. А "компьютеров", с которых сняли снапшоты, будут многие тысячи.
W>Подробнее опиши задачу. Желательно без упрощений и аналогий.
В стартовом посте 1-в-1 мапится на реальную задачу. Замена сущностей на реальные ничего не даст в ключе понимания.
Надо уметь быстро анализировать снапшоты.
Например
— понять, какой каталог не менялся в течении какого времени.
— Посчитать число уникальных файлов.
— посчитать тоже самое, но в разрезе снапошотов по всем компьютерам
— найти отличие в снапшотах/каталогах между компьютерами (например там есть только несколько файлов, которые отличаются, тогда как тысячи остальных одинаковые)
и т.д.
Заранее не могу сказать, какие запросы буду делать. Анализ изменений, анализ похожести машин, анализ подозрительных вкраплений. Как-то так.
Здравствуйте, IID, Вы писали:
IID>Заранее не могу сказать, какие запросы буду делать.
Вот это главное. Предлагаю решать проблемы по мере их возникновения. Уверен, потребности определятся гораздо раньше, чем твоя таблица разрастется до того, что невозможно будет ее перестроить.
А пока можно применить что-нибудь классическое, например SCD Type 2. Будет только две таблицы, снапшоты и "файлы". Обвесь нужными индексами и вперед.
Здравствуйте, IID, Вы писали:
IID>Снапшоты файловой системы, в виде 3х таблиц: IID>1) сами снапшоты IID>2) содержимое файлов (например: имя, хеш, размер) IID>3) ссылка между 1) и 2), собирающая нужные файлы в снапшот. IID>Проблема в том, что хоть записи в 3) весьма компактные, но файлов ОЧЕНЬ много. А вот изменений между снапшотами довольно мало.
Не очень просек в чем проблема. Есть абстрактные опасения, что добавление полей "DELETED" и "DEL_TIMESTAMP" и их обновление будет тормозить? Так надо бы испытать сначала, индексы покрутить, партиции.
Здравствуйте, Dym On, Вы писали:
DO>Не очень просек в чем проблема.
В том что записей СЛИШКОМ много.
DO>Есть абстрактные опасения, что добавление полей "DELETED" и "DEL_TIMESTAMP" и их обновление будет тормозить? Так надо бы испытать сначала, индексы покрутить, партиции.
Да у меня диск быстрее закончится. Эксклюзивно выделенный под БД, 2тб NVMe.
На 2тыс. снапшотах имеем:
— информация о содержимом файлов: 2.8млн записей (838 мб)
— информация о каталогах: 2.6 млн записей (759 мб)
— информация о построении путей: (318 тыс. записей) 49мб
— информация о именах файлов: (124 тыс записей) 2.1 мб
— информация о снапшотах: (2 тыс записей) 0.7мб
— ссылки между снапшотами и каталогами: 78 млн. записей (6800 мб), count(*) на "холодную" выполняется 12 секунд
— ссылки между снапшотами и файлами: 232млн. записей (30000 мб), count(*) на "холодную" выполняется 53 секунды
(пофиг пока на секунды, это можно подтюнить. Они тут больше для подчёркивая избыточности хранения "as is")
Это же абсурд!
Когда реальные данные занимают всего лишь единицы % от обьёма всей базы. А основной размер — это почти одинаковые наборы связей (ссылок).
И что такое 2 тысячи снапшотов ? Их будет минимум в 100 раз больше. Если не в 1000.
Здравствуйте, IID, Вы писали:
IID>В том что записей СЛИШКОМ много. IID> ...
А, кажется понял, твоя идея хранить не сами снепшоты, а дифф от базового/предыдущего. Норм идея, совет тот же, отмакетируй все варианты и сравни. А что важнее: запись или чтение?
Здравствуйте, IID, Вы писали:
IID>Чтение. IID>Более того, можно импортировать в некотором "сыром" виде, а потом оптимизировать, хоть внешним кодом на питон/c++/everything else.
Я сейчас просто фантазирую. Если дифф делать от некоей базы, дальше фигачить диффы именно от базы, при отличии последнего диффа от базы на некоторую величину (ну скажем 20%), делать полное сохранение, назначать новой базой и дальше хранить диффы, но уже от новой базы. Но тут нужно экспериментировать. Придется прикрутить некоторый интеллект, который бы отслеживал различия. Можно с партициями чего-нибудь помутить. Но вообще, постгрес небыстрая СУБД.