用于审计日志记录的数据库设计


151

每当我需要设计一个新的数据库时,我都会花很多时间思考如何设置数据库架构以保留更改的审核日志。

此处已经对此提出了一些问题,但是我不同意在所有情况下都存在一种最佳方法:

我还偶然发现了这篇有关维护数据库更改日志的有趣文章,该文章试图列出每种方法的利弊。它写的很好,并且有有趣的信息,但是这使我的决定更加困难。

我的问题是:是否有我可以使用的参考,也许是一本书或诸如决策树之类的东西,我可以参考这些参考来根据一些输入变量来决定应该走的路,例如:

  • 数据库架构的成熟度
  • 如何查询日志
  • 需要重新创建记录的概率
  • 更重要的是:写入或读取性能
  • 所记录的值的性质(字符串,数字,斑点)
  • 可用存储空间

我知道的方法是:

1.添加创建和修改日期及用户的列

表示例:

  • ID
  • 值_1
  • 值_2
  • 值_3
  • 创建日期
  • 修改日期
  • 由...制作
  • modified_by

主要缺点:我们失去了修改的历史。提交后无法回滚。

2.仅插入表格

表示例

  • ID
  • 值_1
  • 值_2
  • 值_3
  • 已删除(布尔值)
  • 用户

主要缺点:如何保持外键最新?需要巨大的空间

3.为每个表创建一个单独的历史记录表

历史记录表示例:

  • ID
  • 值_1
  • 值_2
  • 值_3
  • 值_4
  • 用户
  • 已删除(布尔值)
  • 时间戳记

主要缺点:需要复制所有审核表。如果架构更改,则也需要迁移所有日志。

4.为所有表创建一个合并的历史表

历史记录表示例:

  • table_name
  • 领域
  • 用户
  • new_value
  • 已删除(布尔值)
  • 时间戳记

主要缺点:如果需要,我是否可以重新创建记录(回滚)?new_value列必须是一个巨大的字符串,以便它可以支持所有不同的列类型。



1
以及使用历史数据库而不是表呢?
Jowen


记录所有按原样执行的(必需)查询是否是一个坏主意?
Dinushan

Answers:


87

一些Wiki平台使用的一种方法是将标识数据和要审核的内容分开。它增加了复杂性,但最终您将获得完整记录的审核记录,而不仅仅是经过编辑的字段列表,然后您必须将其混搭起来以使用户了解旧记录的外观。

因此,例如,如果您有一个称为“ 机会”的表来跟踪销售交易,则实际上将创建两个单独的表:

机会
Opportunities_Content(或类似的内容)

机遇表会让你原本是用来唯一标识的记录,将能容纳你会为你的外键关系引用的主键信息。该Opportunities_Content表将持有你的用户可以更改所有字段,并且您想为其保留审计线索。“ 内容”表中的每个记录都将包含其自己的PK以及“修改者”和“修改日期”数据。在机遇表将包括在最初创建的主记录以及由谁来到当前版本的参考和信息。

这是一个简单的例子:

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

如果Revision是一种身份类型,我可能会将内容表的PK设为PageID和Revision中的多列键。您可以将“修订”列用作FK。然后,您可以像这样通过JOINing提取合并记录:

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

那里可能有一些错误...这简直就是我的头上。但是,它应该使您想到另一种模式。


10
就审计良好方法而言,但对于生产而言,将需要大量时间为数据库中的每个表开发一个单独的审计表,为每个表编写触发器以捕获更改并将其写入审计表。此外,由于每个审计表的结构不同,因此为所有表开发一个审计报告也面临着巨大的挑战。
asim-ishaq

11
如果对于打算管理审核数据库的组织来说,为每个表编写和维护脚本是一个问题,我自然会建议他们聘请经验丰富的DBA或经验丰富,经验丰富的软件工程师来创建审核数据库。
Hardryv

1
PageContent.PageIDFK Page.IDPage.CurrentRevisionFK 是正确的PageContent.Revision吗?这种依赖关系真的循环吗?

2
我投了反对票,因为它没有解决提到的替代方案。它提供了另一个选项,它是针对特定用例的特定解决方案。但是我确实看到了建议设计的优点
acteon

1
我能想到的很少几个字段可以肯定地说不会改变,因此每个实体的所有“主”表最终都只会存在id, revision_id;确实是一个联结表。对我来说这有点臭。与OP(历史记录表/已审核表)中的方法3相比,这具有什么优势?
肯莫尔

14

如果使用的是SQL Server 2008,则可能应考虑更改数据捕获。这是2008年的新功能,可以为您节省大量的工作。


这是SQL 2012更改跟踪信息的链接。msdn.microsoft.com/en-us/library/bb933994.aspx +1使用内置功能,没有意义重新发明轮子。
克里斯,

4
@Chris您是否曾经使用过它?确实,它跟踪所有内容...但是能够从中获取有用的信息完全是另外一回事了。我的自行车不能使用牵引轮。
Jowen

这真的一直真棒。但是,如果像我一样只有SQL Server 的标准版,那您就不走运了:“更改数据捕获仅在EnterpriseDeveloperEnterprise Evaluation版中可用”。
布拉德·图瑞克

6

我不知道任何参考,但是我敢肯定有人写过东西。

但是,如果目的仅仅是记录发生的情况(审计日志的最典型用法),那么为什么不简单地保留所有内容:

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

大概是由触发器来维持的。


我不知道如何在数据库服务器中获取该信息,但是当然可以很容易地从其外部完成。
wallyk'1

5
在我看来,这是与原始问题中所示的第四个选项相同的设计模式。
givanse 2014年

3

我们将为博客应用程序创建一个小的示例数据库。需要两个表:

blog:存储唯一的帖子ID,标题,内容和已删除的标志。 audit:存储具有记录ID,博客文章ID,更改类型(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

我认为没有什么比决策树更重要的了。由于某些利弊(或要求)并不是真正可数的。例如,您如何衡量成熟度?

因此,只需为审核日志排队您的业务需求。尝试预测这些需求在将来会如何变化并生成您的技术需求。现在,您可以将其与优缺点进行比较,然后选择正确/最佳选择。

而且请放心,无论您如何决定,总会有人认为您做出了错误的决定。但是,您做了家庭作业,并且证明了自己的决定是正确的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.