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

151

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

jbochi
источник
связанные: stackoverflow.com/questions/9852703/…
Kaii
1
а как насчет использования базы данных истории вместо таблиц?
Йовен
Может быть, вы могли бы проверить дизайн github.com/airblade/paper_trail
zx1986
Это плохая идея регистрировать все (обязательные) запросы, выполненные как есть?
Динушан

Ответы:

87

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

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

Opportunities
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

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

Джош Андерсон
источник
10
С точки зрения аудита хороший подход, но для производства потребуется много времени, чтобы разработать отдельную таблицу аудита для каждой таблицы в базе данных, записать триггеры для каждой таблицы, чтобы зафиксировать изменения, и записать ее в таблицу аудита. Кроме того, огромная проблема заключается в разработке единого отчета по аудиту для всех таблиц, поскольку каждая таблица аудита отличается по структуре.
асим-ишак
11
Если написание и ведение сценариев для каждой таблицы является проблемой для организации, которая намерена управлять проверенной базой данных, я, естественно, рекомендую нанять либо опытного администратора баз данных, либо очень гибкого и очень опытного инженера-программиста с достаточным опытом создания проверенных баз данных. ,
Hardryv
1
Верно ли, что PageContent.PageIDэто ФК Page.IDи Page.CurrentRevisionФК PageContent.Revision? Эта зависимость действительно круговая?
2
Я проголосовал, так как он не затрагивает упомянутые альтернативы. Это дает еще один вариант, который является очень конкретным решением для очень конкретного случая использования. Но я вижу достоинства предлагаемого дизайна
актеон
1
Я могу думать о очень немногих полях, которые я могу с уверенностью сказать, не изменится, поэтому все «основные» таблицы для каждой сущности в конечном итоге будут просто id, revision_id; на самом деле больше соединительного стола. Это чувствует себя немного вонючей для меня. Какое преимущество это имеет по сравнению с подходом 3 в OP (таблица истории на проверенную таблицу)?
Кенмор
14

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

Рэнди Миндер
источник
Вот ссылка на информацию об отслеживании изменений в SQL 2012. msdn.microsoft.com/en-us/library/bb933994.aspx +1 за использование встроенных функций, нет смысла заново изобретать колесо.
Крис
4
@ Крис, ты когда-нибудь использовал это сам? Действительно, он отслеживает все ... но возможность извлечь из него полезную информацию - это совсем другая история. Не могу использовать колесо трактора для моего велосипеда.
Йовен
Это действительно было бы здорово. Но если у вас есть только стандартная версия SQL Server, как и я, вам не повезло: «Сбор данных изменений доступен только в выпусках Enterprise , Developer и Enterprise Evaluation ».
Брэд Турек
6

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

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

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

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

wallyk
источник
Я не знаю ни одного способа получить это на сервере базы данных, но, конечно, это может быть сделано извне достаточно легко.
Wallyk
5
Мне кажется, что это тот же шаблон дизайна, что и 4-й вариант, показанный в исходном вопросе.
Givanse
3

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

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;
Ажит
источник
2

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

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

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

Питер Шютце
источник