В проекте, над которым я работаю, каждое изменение строк в некоторых таблицах базы данных должно отслеживаться для дальнейшего аудита или отката. Должно быть легко найти, кто изменил строку, с какого IP-адреса и когда, и иметь возможность восстановить предыдущую версию.
Подобная вещь используется, например, Stack Exchange. Когда я изменяю чужой вопрос, можно обнаружить, что я его изменил, и откатить изменения.
Какой общий метод используется для хранения каждого изменения объекта в базе данных , учитывая, что моя текущая схема имеет в основном те же свойства (ниже), что и обычное бизнес-приложение?
- Объекты имеют относительно небольшой размер: это могут быть,
nvarchar(1000)
например, некоторые , но не огромные двоичные объекты двоичных данных, которые хранятся непосредственно на диске и доступны напрямую, а не через Microsoft SQLfilestream
, - Загрузка базы данных довольно низкая, и вся база данных обрабатывается одной виртуальной машиной на сервере,
- Доступ к предыдущим версиям не должен быть таким же быстрым, как доступ к последней версии, но все же должен быть актуальным и не слишком медленным2.
<ТЛ-дг>
Я думал о следующих случаях, но у меня нет никакого реального опыта с такими сценариями, поэтому я услышал бы мнения других:
Сохраните все в одной таблице, различая строки по идентификатору и версии. ИМО, это серьезно глупо и рано или поздно повредит уровню производительности. При таком подходе также невозможно установить другой уровень безопасности для последних элементов и трассировки версий. Наконец, каждый запрос будет сложнее написать. На самом деле, чтобы получить доступ к актуальным данным, я был бы вынужден сгруппировать все по идентификатору и получить в каждой группе последнюю версию.
Сохраните последнюю версию в одной таблице и при каждом изменении копируйте устаревшую версию в другую таблицу в другой схеме. Недостаток в том, что каждый раз мы храним каждое значение, даже если оно не изменилось. Установка неизмененных значений в
null
не является решением, так как я также должен отслеживать, когда значение изменяется наnull
или сnull
.Сохраните последнюю версию в одной таблице, а список измененных свойств с их предыдущими значениями - в другой таблице. Кажется, у этого есть два недостатка: самый важный из них заключается в том, что единственный способ сортировки разнородных типов предыдущих значений в одном столбце - это иметь
binary(max)
. Во-вторых, я полагаю, что было бы сложнее использовать такую структуру при отображении предыдущих версий пользователю.Сделайте то же самое, что и в двух предыдущих пунктах, но сохраните версии в отдельной базе данных. С точки зрения производительности, это может быть интересно, чтобы избежать замедления доступа к последним версиям, если предыдущие версии находятся в той же базе данных; тем не менее, я считаю, что это преждевременная оптимизация, и ее следует проводить только в том случае, если есть доказательство того, что наличие более старых и последних версий в одной и той же базе данных является узким местом.
</ ТЛ-дг>
¹ Например, было бы неприемлемо сохранять изменения в файле журнала, как это делается для журналов HTTP, и сбрасывать данные из журнала в базу данных ночью, когда нагрузка на сервер самая низкая. Информация о различных версиях должна быть доступна немедленно или почти сразу; задержка в несколько секунд приемлема.
² Доступ к информации осуществляется не очень часто и только определенной группой пользователей, но все же было бы недопустимо заставлять их ждать 30 секунд для отображения списка версий. Опять же, задержка в несколько секунд является приемлемой.
источник
Ответы:
Обычный способ ведения журнала аудита такого рода состоит в том, чтобы иметь теневую таблицу и регистрировать изменения с помощью триггеров на базовой таблице, которую вы проверяете. Другие таблицы могут быть размещены на другом физическом диске, если вам это необходимо для повышения производительности, и вы можете размещать на них индексы, если вам необходимо поддерживать быстрый поиск данных.
Таблицы будут иметь примерно ту же структуру, что и исходные таблицы, но будут иметь столбец даты и времени, когда произошло изменение, и маркер того, была ли строка вставлена, изменена или удалена. Упорядочивание версий может быть выполнено с помощью отметки времени.
Изменить дату можно, установив для столбца datetime значение NULL со значением по умолчанию getdate (); пользовательский столбец аудита будет захватывать пользователя с ненулевым столбцом по умолчанию Suser_Sname (). Если предположить, что в сеансе выдают себя за действительного пользователя, это будет идентифицировать личность пользователя, вносящего изменения.
База данных не может знать IP-адрес, подключенный к веб-серверу. Приложению придется явно захватывать и регистрировать IP-адрес транзакции.
Если у вас есть большое количество таблиц, которые вы хотите проверить, вы можете использовать метаданные из системного словаря данных для программной генерации триггеров.
Это решение, безусловно, является лучшим по нескольким причинам:
Он фиксирует любые изменения в таблице, а не только те, которые внесены приложением.
Таблицы аудита могут быть помещены на другой набор дисков, чтобы уменьшить нагрузку ввода-вывода на ваши основные таблицы.
Вы можете использовать представление, основанное на объединении таблицы и таблицы журнала аудита, в котором будет показана вся история, включая текущую версию.
При необходимости вы можете индексировать таблицы журнала аудита, чтобы пользователи аудита могли отвечать на них быстро. Как обычно, выбор индекса - это компромисс между производительностью запросов и издержками на обновление.
источник
Я знаю о многих системах CMS (включая Wordpress), которые используют одну таблицу для хранения всех версий данных. Но опять же, они должны сделать это только для таблицы, в которой есть сообщения в блоге. Смотрите структуру базы данных Wordpress .
Кроме того, количество записей и количество проверок, которые проходит каждая строка, будут играть важную роль в вашем решении.
источник
О версии CMS; для drupal он создает специальную таблицу для каждого поля сущности, в которой хранится старое значение; такая концепция позволяет вам аккуратно манипулировать вашими данными, но я думаю, что это дорого, мое собственное решение состоит в том, чтобы преобразовать мой объект в формат xml и сохранить его в виде строки с другими полями (changetime, id ...)
источник