Подтвердить что ты не робот

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

У меня есть система, написанная с использованием 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)
...

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

4b9b3361

Ответ 1

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

Кстати, существует реализация для "версий" табличных данных в доктрине 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 схему и данные в исходной таблице через обновление (например, обновление программного обеспечения), вы должны обеспечить, чтобы одни и те же данные или схемы были применены к таблице истории и ее данным. В противном случае вы столкнетесь с проблемой при возврате к более старой версии набора записей.
  • В реальной реализации вы хотели бы знать, какой пользователь изменил данные. Чтобы эта ревизивно безопасная запись пользователя никогда не удалялась из таблицы пользователей. Вы должны просто отключить учетную запись с флагом.
  • Обычно одно действие пользователя включает несколько таблиц. В реализации реального мира вам также необходимо будет отслеживать, какие изменения в нескольких таблицах относятся к одной пользовательской транзакции, а также в каком порядке. В реальном случае вы хотели бы вернуть все изменения одной транзакции вместе, в обратном порядке. Для этого потребуется дополнительная таблица ревизий, которая отслеживает пользователей и транзакции и сохраняет свободную связь со всеми этими отдельными ревизиями в таблицах истории.

Преимущества:

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

Достоинства:

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

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


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

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

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

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

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

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

Преимущества:

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

Достоинства:

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

Вывод:

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

Ответ 2

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

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

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

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