Database design for audit logging
每次我需要设计一个新的数据库时,我都会花很多时间思考如何设置数据库模式以保存审核日志变化。
关于这件事我已经问了一些问题,但我不同意对于所有场景都有一个最佳方法:
- 数据库修订设计
- 更改日志审核数据库表的最佳设计
- 审计跟踪数据库设计思路
我还偶然发现了这篇有趣的文章,它维护了一个数据库更改日志,试图列出每种方法的优缺点。它写得很好,有有趣的信息,但它使我的决定更加困难。
我的问题是:是否有一个参考可以使用,可能是一本书或类似于决策树的东西,我可以参考它来决定我应该根据一些输入变量,如:
- 数据库模式的成熟度
- 如何查询日志
- 需要重新创建记录的概率
- 更重要的是:读写性能
- 正在记录的值的性质(字符串、数字、Blob)
- 可用存储空间
我知道的方法是:
1。为创建和修改日期和用户添加列
表示例:
- 身份证件
- 价值观
- 值2
- 值3
- 创建日期
- 修改日期
- 创建者
- 修改人
主要缺点:我们失去了修改的历史。提交后无法回滚。
2。仅插入表格
表示例:
- 身份证件
- 价值观
- 值2
- 值3
- 从
- 到
- 已删除(布尔值)
- 用户
主要缺点:如何保持外键的最新状态?需要巨大的空间
三。为每个表创建单独的历史记录表
历史表示例:
- 身份证件
- 价值观
- 值2
- 值3
- 价值观
- 用户
- 已删除(布尔值)
- 时间戳
主要缺点:需要复制所有已审核的表。如果架构发生更改,则也需要迁移所有日志。
4。为所有表创建合并历史记录表
历史表示例:
- 表名称
- 领域
- 用户
- 新的价值
- 已删除(布尔值)
- 时间戳
主要缺点:如果需要,我可以很容易地重新创建记录(回滚)吗?新的"值"列需要是一个巨大的字符串,以便它可以支持所有不同的列类型。
一些wiki平台使用的一种方法是分离识别数据和正在审核的内容。它增加了复杂性,但最终您会得到完整记录的审计跟踪,而不仅仅是被编辑的字段列表,然后您必须混合这些字段才能让用户了解旧记录是什么样子的。
例如,如果您有一个名为Opportunities的表来跟踪销售交易,那么实际上您将创建两个单独的表:
机会机会内容(或类似的内容)
Opportunities表将包含您将用于唯一标识记录的信息,并包含您将为外键关系引用的主键。Opportunities_内容表将包含您的用户可以更改的所有字段,您希望对这些字段进行审计跟踪。内容表中的每个记录都将包含自己的pk以及修改者和修改日期数据。Opportunities表将包括对当前版本的引用,以及有关最初创建主记录的时间和创建者的信息。
下面是一个简单的例子:
1 2 3 4 5 6 | 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 |
号
内容:
1 2 3 4 5 6 7 8 9 | 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也可以。您将使用修订列作为FK。然后,您可以通过如下方式加入合并记录:
1 2 | SELECT * FROM Page JOIN PageContent ON CurrentRevision = Revision AND ID = PageID |
。
上面可能有一些错误……这是我的头上。不过,它应该给你一个备选模式的概念。
如果您使用的是SQL Server 2008,那么您可能应该考虑更改数据捕获。这是2008年的新情况,可以为您节省大量的工作。
我不知道有什么推荐信,但我确信有人写了些东西。
但是,如果目的仅仅是记录审计日志最典型的用途,那么为什么不简单地保留所有内容:
1 2 3 4 5 6 7 8 9 10 | timestamp username ip_address procedureName (if called from a stored procedure) database table field accesstype (insert, delete, modify) oldvalue newvalue |
这大概是由一个触发器维持的。
我们将为一个博客应用程序创建一个小的示例数据库。需要两个表:
1 2 3 4 5 6 7 8 | 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创建
1 2 3 4 5 6 7 8 9 10 11 | 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; |
。
我认为没有什么能比得上决策树。因为一些利弊(或者需求)实际上是不可数的。例如,你如何衡量成熟度?
所以,只需为您的审计日志记录整理您的业务需求。试着预测这些需求在未来会如何变化,并生成您的技术需求。现在,您可以将其与优缺点进行比较,并选择正确/最佳的选项。
当然,不管你怎么决定,总会有人认为你做了错误的决定。然而,你做了家庭作业,你证明了你的决定是正确的。