Разработка базы данных для ведения журнала аудита

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

Здесь уже задавались некоторые вопросы по этому поводу, но я не согласен с тем, что существует единый лучший подход для всех сценариев:

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

Мой вопрос: есть ли ссылка, которую я могу использовать, может быть, книга или что-то вроде дерева решений, на которое я могу сослаться, чтобы решить, каким путем мне идти, основываясь на некоторых входных переменных, например:

  • Зрелость схемы базы данных
  • Как будут запрашиваться журналы
  • Вероятность того, что будет необходимо воссоздать записи
  • Что важнее: написать или прочитать производительность
  • Природа значений, которые регистрируются (строка, числа, капли)
  • Доступное место для хранения

Подходы, которые я знаю:

1. Добавить столбцы для созданной и измененной даты и пользователя

Пример таблицы:

  • Я бы
  • значение_1
  • значение_2
  • VALUE_3
  • Дата создания
  • MODIFIED_DATE
  • создано
  • модифицирован

Основные минусы: мы теряем историю модификаций. Не могу откатиться после коммита.

2. Вставьте только таблицы

Пример таблицы:

  • Я бы
  • значение_1
  • значение_2
  • VALUE_3
  • от
  • в
  • удалено (логическое)
  • пользователь

Основные минусы: как поддерживать актуальность внешних ключей? Нужно огромное пространство

3. Создайте отдельную таблицу истории для каждой таблицы

Пример таблицы истории:

  • Я бы
  • значение_1
  • значение_2
  • VALUE_3
  • value_4
  • пользователь
  • удалено (логическое)
  • timestamp

Основные минусы: необходимо дублировать все проверенные таблицы. Если схема изменится, потребуется перенести и все журналы.

4. Создайте сводную таблицу истории для всех таблиц

Пример таблицы истории:

  • table_name
  • поле
  • пользователь
  • new_value
  • удалено (логическое)
  • timestamp

Основные минусы: Смогу ли я легко воссоздать записи (откат), если это необходимо? Столбец new_value должен быть огромной строкой, чтобы он мог поддерживать все типы столбцов.

Ответ 1

Один из методов, который используется несколькими вики-платформами, заключается в разделении идентифицирующих данных и контента, который вы проверяете. Это добавляет сложности, но в результате вы получаете контрольный журнал с полными записями, а не просто списки полей, которые были отредактированы, и которые вам нужно будет затем объединить, чтобы дать пользователю представление о том, как выглядела старая запись.

Например, если у вас есть таблица " Возможности для отслеживания сделок продажи", вы фактически создадите две отдельные таблицы:

возможности
Opportunities_Content (или что-то в этом роде)

Таблица Opportunities будет содержать информацию, которую вы будете использовать для уникальной идентификации записи, и будет содержать первичный ключ, на который вы будете ссылаться для ваших отношений с внешним ключом. Таблица Opportunities_Content будет содержать все поля, которые могут изменить ваши пользователи и для которых вы хотите вести контрольный журнал. Каждая запись в таблице содержимого будет включать в себя свой собственный PK и данные о дате изменения и изменения. Таблица Opportunities будет содержать ссылку на текущую версию, а также информацию о том, когда основная запись была создана и кем.

Вот простой пример:

CREATE TABLE dbo.Page(  
    ID int PRIMARY KEY,  
    Name nvarchar(200) NOT NULL,  
    CreatedByName nvarchar(100) NOT NULL, 
    CurrentRevision int NOT NULL, 
    CreatedDateTime datetime NOT NULL

И содержание:

CREATE TABLE dbo.PageContent(
    PageID int NOT NULL,
    Revision int NOT NULL,
    Title nvarchar(200) NOT NULL,
    User nvarchar(100) NOT NULL,
    LastModified datetime NOT NULL,
    Comment nvarchar(300) NULL,
    Content nvarchar(max) NOT NULL,
    Description nvarchar(200) NULL

Я бы, вероятно, сделал PK таблицы содержимого ключом из нескольких столбцов от PageID и Revision при условии, что Revision был типом идентификации. Вы бы использовали колонку Revision в качестве FK. Затем вы извлекаете консолидированную запись, присоединяясь так:

SELECT * FROM Page
JOIN PageContent ON CurrentRevision = Revision AND ID = PageID

Там могут быть некоторые ошибки... это с моей головы. Это должно дать вам представление об альтернативном паттерне.

Ответ 2

Если вы используете SQL Server 2008, вам, вероятно, стоит подумать об изменении Data Capture. Это новое для 2008 года и может сэкономить вам значительную часть работы.

Ответ 3

Я не знаю никаких ссылок, но я уверен, что кто-то что-то написал.

Однако, если целью является просто иметь запись о том, что произошло - наиболее типичное использование журнала аудита, то почему бы просто не сохранить все:

timestamp
username
ip_address
procedureName (if called from a stored procedure)
database
table
field
accesstype (insert, delete, modify)
oldvalue
newvalue

Предположительно это поддерживается триггером.

Ответ 4

Я думаю, что нет ничего похожего на дерево решений. Поскольку некоторые плюсы и минусы (или требования) на самом деле не являются счетными. Как вы, например, измеряете зрелость?

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

И будьте уверены, неважно, как вы решаете, всегда будет кто-то, кто думает, что вы приняли неправильное решение. Тем не менее, вы сделали домашнее задание и оправдали свое решение.

Ответ 5

Хорошо создайте небольшую базу примеров для приложения для ведения блогов. Требуются две таблицы:

blog: сохраняет уникальный идентификатор сообщения, заголовок, содержимое и удаленный флаг. audit: хранит базовый набор исторических изменений с идентификатором записи, идентификатором сообщения блога, типом изменения (NEW, EDIT или DELETE) и датой/временем этого изменения. Следующий SQL создает blog и индексирует удаленный столбец:

CREATE TABLE `blog` (
    `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
    `title` text,
    `content` text,
    `deleted` tinyint(1) unsigned NOT NULL DEFAULT '0',
    PRIMARY KEY (`id`),
    KEY `ix_deleted` (`deleted`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='Blog posts';

Следующий SQL создает таблицу audit. Все столбцы индексируются и внешний ключ определен для audit.blog_id, который ссылается на blog.id. Поэтому, когда мы физически удаляем запись в блоге, также удаляется его полная история аудита.

CREATE TABLE `audit` (
    `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
    `blog_id` mediumint(8) unsigned NOT NULL,
    `changetype` enum('NEW','EDIT','DELETE') NOT NULL,
    `changetime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`),
    KEY `ix_blog_id` (`blog_id`),
    KEY `ix_changetype` (`changetype`),
    KEY `ix_changetime` (`changetime`),
    CONSTRAINT `FK_audit_blog_id` FOREIGN KEY (`blog_id`) REFERENCES `blog` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;