Pull to refresh

Comments 28

Работал с нечто подобным. В вашем варианте вы создаете отдельно таблицу логгирования на каждую таблицу данных? В PostgreSQL нет триггеров событий? Или я не внимательно читал. В Oracle есть такое. В любом случае спасибо за статью, сейчас работаю с PostgreSQL и была идея реализовать у себя логгирование на основе лог-таблиц. Пока делаю через первый вариант, описанный в статье.
UPDATE

Увидел создание таблицы. Я копал в сторону триггеров событий, что бы при создании таблицы данных создавалась и лог таблица для нее. Правда что-то как-то заморчоено все с этими триггерами в PostgreSQL.

Насколько проседает производительность при вставках и изменениях в устоявшихся режимах?

Вставки и изменения у нас обернуты в функции с достаточно сложной логикой, на их фоне влияние двойной записи на диск стремится к нулю.
Сильная потеря производительности может получиться только на таблицах с частыми обновлениями, или на таблицах с большими записями(т.к. там используется механизм TOAST и при обновлении такой строки постгрес сильно оптимизирует эту операцию)
Спасибо за статью, а можно ли читать изменения из WAL? На одной из конференций я слышал про такую возможность стороннего плагина (берем из WAL и кладем в очередь), но что-то найти не могу.
Нашел вот такую штуку
https://github.com/michaelpq/pg_plugins/tree/master/decoder_raw — читает WAL и генерит SQL стейтменты
На мой взгляд, идея не самя удачная — WAL логи не слишком хорошо подходят для извлечения из них запросов, это лог изменения данных на диске. Из самих WAL файлов (без наличия реплики с реплицированной базой) эту информацию извлечь невозможно.
Не знаю какая у вас структура таблиц, но хотелось узнать какой был просед производительности при внедрении?
Все таки используется динамический SQL при каждой операции с таблицей.
В нашем случае проседание производительности практически нулевое, т.к. логгируются таблицы, которые не часто и не сильно изменяются.
Динамический SQL используется не при каждой операции, а только при изменении данных.
К тому же при вставке данных в таблицу логов не производится никаких проверок(внешние ключи)
Проседание производительности может получиться при массовой вставке, но мы отключаем логгирование в таких случаях
А каким образом отключаете?
В рамках транзакции удаляете триггер, а потом добавляете заново?
А какие действие предусмотрены на случай изменения схемы данных?
Я к тому, что можно ли было пользоваться форматом JSON для хранения логов конкретной записи? Сама «ячейка» логов (если так можно выразиться) является JSON-массивом, куда вы только добавляете данные.Такой способ вполне может переварить и изменение колонок и изменение данных логов через триггеры. И таблица логов вполне себе может быть одна. (не знаю, насколько критична эта схема будет по производительности под хорошей нагрузкой).
Как по мне главный минус такого подхода это большая куча в одной таблице. Зато бесспорный плюс то, что лог будет удобнее читать при работе сразу с несколькими таблицами в одной функции.
Как вариант можно добавить дробление таблицы с логами горизонтально по дате или как уже тут проделано по имени таблицы.
При изменении схемы данных, возникнет исключение и таблица с логами будет пересоздана.
Насчет использования JSON не задумывались, можно попробовать
Т.е. при изменении схемы данных все старые логи теряются?
Нет, старая таблица с логами переименовывается и создается новая.
Знакомая тема. И всё же чем вам не угодил вариант с дополнительными служебными полями таблиц на триггерах? Ограниченность лога я так понимаю?
И ещё вопрос про обращение к таблице. Вызов напрямую или используете view?

Есть два основных варианта логирования изменений без дополнительных таблиц:


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

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

Спасибо, мне это известно) Вопрос больше касался почему не хватало просто наблюдать за информацией о том что со строкой происходит. В требованиях выданы лишь качественные характеристики к системе логирования, но не было сказано чего именно от лога ожидают. У меня в своё время была такая же система из служебных полей, по задумке автора интерес представляло только дата изменения и кто поменял, если запись удалялось то это даже не интересовало.
Причём как по мне тут прежде всего надо разобраться с самим понятием, а что есть «лог». И если уж быть честным, то у меня язык не поворачивается назвать такую систему логом) больше похоже на метаинформацию фаловой системы. Она не столь нужна для лога, сколько для быстрого получения информации которую вполне можно взять как раз из логирования, но скорость при этом критична.
по задумке автора интерес представляло только дата изменения и кто поменял, если запись удалялось то это даже не интересовало

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

Создание новых записей в той же таблице и пометка их как архивные это не лог и с постгрессом здесь схожесть только в том, что записи постгресса хранятся кортежами которые не меняются, а просто создаются новые. При этом если включить autovacuum, то старые записи сотрёт, но это так вкратце и не совсем точное описание)
Что же касается множественного набора записей, то это в первую очередь прерогатива архитектуры.
По такому принципу как правило строятся системы версий. Вкратце, если запись создана с такими и только такими параметрами, то любое их изменение влечёт изменение зависимых от неё (или ссылающихся на неё) записей.
По такому принципу строят, к примеру, международный словарь стран и их кодов ISO-3166. Стоит стране поменять площадь государства как сразу обновляется её номер.

Проще логи хранить отдельно всегда, не пытаясь их прилепить к основной записи.

А тут я с Вами полностью согласен, так как цель логов не только сохранить предыдущее состояние объекта, но и дополнительную метаинформацию и при этом ни в коем случае не влиять на саму информацию своим существованием.
Еще раз нипишу минусы доп полей
1.Удаленные записи продолжают лежать в таблице(нужно вносить изменения в существующие функции, дополнительные расходы при выборках)
2.Если одна запись обновляется неоднократно, теряется история изменений
3.Могут возникнуть ошибки при добавлении новых ограничений на таблицу, т.к. логгировать предполагалось не все таблицы, то логгируемая таблица вполне может ссылаться на нелоггируемую
Приложение обращается к таблицам через обертки из функций, сами функции обращаются напрямую в таблицы.
Приложение обращается к таблицам через обертки из функций, сами функции обращаются напрямую в таблицы.


Я имел ввиду просмотр данных. Для просмотра данных так же используете функции с возвратом? Если да, то это напоминает механизм доп контроля над данными таблиц доступных оператору.

эх извращаться так извращаться.


А что мешало в первом варианте что вы писали сделать следующее:


  1. Переименовать таблицу которую логируете, ну любое имя или префикс.
  2. Создать новое представление (view) с именем таблицы которое было до переименования чтобы он брал данные из таблицы без флага _deleted например
  3. Повесить тригеры на изменения данных во view (insert, update, delete)

Итого получаем, старые скрипты работают с view вместо таблицы и даже не замечают подмены, действия на данными во view обрабатываются тригерами

Такой вариант тоже пробовали, проблема в том, что некоторые функции работают напрямую с системными таблицами(pg_class/pg_attribute/pg_constraint) и на логгируемых таблицах они ломались.
К тому в варианте с представлениями удаленные/обновленные записи будут влиять на производительность при SELECT`ах
Класс, сделали аудит «на триггерах», потому что скорость IUD настолько мизерная, что триггер — не bottleneck.

Эт понятно как его делать, было бы интереснее посмотреть, как проходил ваш поиск, какие решения рассматривали. Какие были бизнес-требования (хотели параметры «до» и «после» или достаточно было SQL запросов — по ним и так понятно, что было до, а что стало)?

Кстати, https://github.com/pgaudit/pgaudit/blob/master/README.md — не смотрели или не подошел?
Неэффективный способ.
Аргументы.
1. При обновлении одной записи приходится сохранять всю запись.
2. Управление историей требует специальных инструментов и обработки каждой отдельной таблицы.
3. Изменение структуры таблицы требует изменения структуры таблицы лога, что опять же делается для каждой таблицы одинаково и в вашем вариенте не зависит от размера таблицы.

Как избавится от этих проблем можно посмотреть в Ultimate 2C
Sign up to leave a comment.

Articles