Храните все изменения данных с каждой детали (например, Stackru)

У меня есть система, написанная с использованием Codeigniter и в качестве базы данных с использованием MySQL. В системе есть пользователь, группы пользователей с различными привилегиями и т. Д. Имеют множество таблиц mysql, которые имеют множество отношений.

Некоторые из таблиц у меня есть:

  • Предметы
  • контракты
  • клиенты
  • товары
  • product_features
  • заказы
  • order_features
  • order_products
  • так далее...

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

A user changed product features with id of A8767
B user added new customer with id 56
C user edited content of orderlist
A user added new product (id: A8767) to order (id: or67)
...

Я хочу сохранить все изменения, которые сделаны с каждой деталью, например, историю редактирования вопроса Stackru. Я могу думать о log_table дизайн, чтобы сохранить все изменения данных из различных таблиц. Есть ли способ, учебник, движок, плагин, чтобы сделать это? Только я могу думать, делать дубликаты из каждой таблицы и хранить изменения в них, но я не думаю, что это хороший способ.

2 ответа

Решение

Я думал об этом некоторое время и могу думать только о двух способах сделать это. Оба могут работать полностью прозрачно, когда встроены в абстрактный слой данных / модель.

Кстати, в доктрине ORM Mapper есть реализация "версионных" данных таблиц. Посмотрите этот пример в их документах. Может быть, это соответствует вашим потребностям, но это не подходит моим. Кажется, что удаляются все исторические данные при удалении исходной записи, что делает ее не очень безопасной для редактирования.

Вариант A: иметь копию каждой таблицы для хранения данных редакции

Допустим, у вас есть простая таблица контактов:

CREATE TABLE contact (
    id INT NOT NULL auto_increment,
    name VARCHAR(255),
    firstname VARCHAR(255),
    lastname VARCHAR(255),
    PRIMARY KEY (id)
)

Вы должны создать копию этой таблицы и добавить данные ревизии:

CREATE TABLE contact_revisions (
    id INT NOT NULL,
    name VARCHAR(255),
    firstname VARCHAR(255),
    lastname VARCHAR(255),
    revision_id INT auto_increment,
    type ENUM('INSERT', 'UPDATE', 'DELETE') NOT NULL,
    change_time DEFAULT current_timestamp,
    PRIMARY KEY(revision_id)
)

Следить за INSERT а также UPDATE с помощью AFTER триггеры. Для каждой новой редакции данных в оригинале вставьте копию новых данных в таблицу редакций и установите модификацию type должным образом.

Чтобы войти DELETE безопасная версия, вы также должны вставить новую строку в таблицу истории! Для этого вы должны использовать BEFORE DELETE вызвать и сохранить последние значения, прежде чем они будут удалены. В противном случае вам придется удалить каждый NOT NULL ограничение в таблице истории, а также.

Некоторые важные замечания относительно этой реализации

  • Для таблицы истории вы должны отбросить каждый UNIQUE KEY (здесь: PRIMARY KEY) из таблицы редакций, поскольку у вас будет один и тот же ключ несколько раз для каждой редакции данных.
  • Когда ты ALTER Схема и данные в исходной таблице через обновление (например, обновление программного обеспечения). Вы должны убедиться, что те же данные или исправления схемы применяются к таблице истории и ее данным. В противном случае у вас возникнут проблемы при возврате к более старой версии набора записей.
  • В реальной реализации вы захотите узнать, какой пользователь изменил данные. Чтобы обеспечить ревизионную безопасность, пользовательскую запись никогда не следует удалять из таблицы users. Вы должны просто отключить учетную запись с флагом.
  • Обычно одно действие пользователя включает в себя более одной таблицы. В реальной реализации вы также должны отслеживать, какие изменения в нескольких таблицах относятся к одной пользовательской транзакции, а также в каком порядке. В реальном случае вы бы хотели отменить все изменения одной транзакции вместе в обратном порядке. Для этого потребуется дополнительная таблица ревизий, которая отслеживает пользователей и транзакции и имеет слабую связь со всеми этими отдельными ревизиями в таблицах истории.

Выгоды:

  • полностью в базе данных, независимо от кода приложения. (ну, не тогда, когда важно отслеживать пользовательские транзакции. Это потребовало бы некоторой логики за пределами одного запроса)
  • все данные в их исходном формате, без явных преобразований типов.
  • хорошие показатели по поиску в ревизиях
  • легкий откат. Просто сделай простое INSERT .. ON DUPLICATE KEY UPDATE .. заявление на исходную таблицу, используя данные из ревизии, которую вы хотите откатить.

Существо дела:

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

Как уже говорилось выше, доктриныversionable делает что-то похожее.


Вариант B: иметь центральную таблицу журнала изменений

Предисловие: плохая практика, показана только для иллюстрации альтернативы.

Этот подход сильно зависит от логики приложения, которая должна быть скрыта в уровне данных / модели.

У вас есть центральная таблица истории, которая отслеживает

  • Кто сделал
  • когда
  • изменить, вставить или удалить
  • какие данные
  • в какой области
  • из какого стола

Как и в другом подходе, вы также можете отслеживать, какие отдельные изменения данных принадлежат одному действию / транзакции пользователя и в каком порядке.

Выгоды:

  • нет необходимости синхронизировать с исходной таблицей при добавлении полей в таблицу или создании новой таблицы. он масштабируется прозрачно.

Существо дела:

  • плохая практика с использованием простого значения = хранилище ключей в базе данных
  • плохая производительность поиска из-за неявных преобразований типов
  • может замедлить общую производительность приложения / базы данных, когда центральная таблица истории становится узким местом из-за блокировок записи (это относится только к конкретным механизмам с блокировками таблиц, т.е. MyISAM)
  • Реализовать откаты намного сложнее
  • возможные ошибки преобразования данных / потеря точности из-за неявного преобразования типов
  • не отслеживает изменения, когда вы напрямую обращаетесь к базе данных где-то в вашем коде, а не используете уровень модели / уровень данных, и забываете, что в этом случае вы должны записывать в журнал изменений вручную. Может быть большой проблемой при работе в команде с другими программистами.

Заключение:

  • Вариант B может быть очень удобен для небольших приложений, так как просто "заглядывает", когда он просто для регистрации изменений.
  • Если вы хотите вернуться назад во времени и иметь возможность легко сравнить различия между исторической ревизией 123 и ревизией 125 и / или вернуться к старым данным, то вариант А - трудный путь.

Как насчет использования общей таблицы обновлений универа. Поля таблицы должны содержать эти значения:

user,event,date,table,field,new value

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

Значения и вставка могут быть созданы с помощью некоторой функции из общего запроса.

Другие вопросы по тегам