审计日志的数据库设计[关闭]

Posted

技术标签:

【中文标题】审计日志的数据库设计[关闭]【英文标题】:Database design for audit logging [closed] 【发布时间】:2011-01-02 04:21:31 【问题描述】:

每次我需要设计一个新的数据库时,我都会花费一些时间 思考我应该如何设置数据库模式以保留审计日志 变化。

这里已经提出了一些关于此的问题,但我不同意 所有场景都有一个最佳方法:

Database Design For Revisions Best design for a change log auditing database table Ideas on database design for capturing audit trails

我还偶然发现了这个interesting article on Maintaining a Log of Database Changes,它试图列出每种方法的优缺点。它写得很好,包含有趣的信息,但它让我的决定更加困难。

我的问题是: 有没有我可以使用的参考资料,可能是一本书或类似决策树之类的东西,我可以参考这些参考资料来决定我应该走哪条路 输入变量,例如:

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

我知道的方法是:

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

表格示例:

身份证 value_1 value_2 value_3 创建日期 修改日期 created_by modified_by

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

2。仅插入表格

Table example:

身份证 value_1 value_2 value_3 来自 到 已删除(布尔值) 用户

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

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

历史表示例:

身份证 value_1 value_2 value_3 value_4 用户 已删除(布尔值) 时间戳

主要缺点:需要复制所有已审核的表。如果架构发生变化,也需要迁移所有日志。

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

历史表示例:

表名 字段 用户 新值 已删除(布尔值) 时间戳

主要缺点:如果需要,我能否轻松地重新创建记录(回滚)? new_value 列需要是一个巨大的字符串,这样才能支持所有不同的列类型。

【问题讨论】:

相关:***.com/questions/9852703/… 用历史数据库代替表格怎么样? 也许你可以看看github.com/airblade/paper_trail的设计 按原样记录所有(必需)执行的查询是不是一个坏主意? 【参考方案1】:

一些 wiki 平台使用的一种方法是将识别数据和您正在审核的内容分开。它增加了复杂性,但您最终会得到完整记录的审计跟踪,而不仅仅是已编辑的字段列表,您必须将它们混搭以让用户了解旧记录的样子。

因此,例如,如果您有一个名为 Opportunities 的表来跟踪销售交易,您实际上会创建两个单独的表:

机会Opportunities_Content(或类似的东西)

Opportunities 表将包含您用来唯一标识记录的信息,并包含您为外键关系引用的主键。 Opportunities_Content 表将包含您的用户可以更改以及您希望保留审计跟踪的所有字段。 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

如果 Revision 是一种身份类型,我可能会将内容表的 PK 设置为来自 PageID 和 Revision 的多列键。您将使用 Revision 列作为 FK。然后,您可以像这样通过 JOINing 拉出合并记录:

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

上面可能有一些错误......这不是我的想法。不过,它应该让您了解另一种模式。

【讨论】:

就审计好的方法而言,但对于生产而言,为数据库中的每个表开发单独的审计表、为每个表编写触发器以捕获更改并将其写入审计表将花费大量时间。此外,由于每个审计表的结构不同,因此为所有表开发单一审计报告是一个巨大的挑战。 如果为每个表编写和维护脚本对于打算管理已审计数据库的组织来说是一个问题,我自然会建议他们聘请经验丰富的 DBA 或高度灵活且经验丰富的软件- 具有创建审计数据库的足够经验的工程师。 PageContent.PageIDPage.ID 的 FK 和 Page.CurrentRevisionPageContent.Revision 的 FK 是否正确?这种依赖真的是循环的吗? 我投了反对票,因为它没有解决提到的替代方案。它提供了另一种选择,它是针对非常具体的用例的非常具体的解决方案。但我确实看到了建议设计的优点 我能想到的字段很少,我可以自信地说不会改变,所以每个实体的所有“主”表最终都会变成id, revision_id;更多的是一个连接表,真的。这让我感觉有点臭。与 OP 中的方法 3(每个审计表的历史表)相比,这有什么优势?【参考方案2】:

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

【讨论】:

这里是 SQL 2012 更改跟踪信息的链接。 msdn.microsoft.com/en-us/library/bb933994.aspx +1 使用内置功能,没有必要重新发明***。 @Chris 你自己用过吗?事实上,它会跟踪一切……但能够从中获取有用的信息是另一回事。我的自行车不能使用拖拉机车轮。 这真的太棒了。但是如果你像我一样只有 Standard 版本的 SQL Server,那你就不走运了:“变更数据捕获仅在 Enterprise 中可用,Developer 和 Enterprise Evaluation 版”。 重要的是要提到更改数据捕获不允许您传递基本信息,例如 WHO 进行了一些更改。您必须创建一个触发器来捕获该信息。在我看来,由于您必须依赖触发器,因此拥有自己的审计触发器更有意义,它适用于任何 SQL Server 版本。【参考方案3】:

我不知道有什么参考资料,但我确定有人写了一些东西。

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

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

大概这是由触发器维护的。

【讨论】:

我不知道有什么方法可以在数据库服务器中获取它,但当然可以很容易地从它外部完成。 在我看来,这与原始问题中显示的第四个选项的设计模式相同。【参考方案4】:

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

blog:存储唯一的帖子 ID、标题、内容和已删除标志。 audit:存储一组基本的历史更改,包括记录 ID、博客文章 ID、更改类型(新、编辑或删除)以及更改的日期/时间。 以下 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 表。所有列都被索引,并且为引用 blog.id 的 audit.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;

【讨论】:

【参考方案5】:

我认为没有什么能比得上决策树了。由于某些优点和缺点(或要求)并不是真正可数的。例如,您如何衡量成熟度?

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

请放心,无论您如何决定,总会有人认为您做出了错误的决定。但是,您做了功课并证明了您的决定。

【讨论】:

【参考方案6】:

我正在使用以下结构:

id  int
user_id int
system_user_id  int
tenant_id   int
db_name varchar
model_name  varchar
model_primary_key   int
model_attributes    text
created_at  timestamp
ip  varchar
session_id  varchar
request_id  varchar
comments    text

到目前为止,在大约 3.62 亿条记录、多租户、多数据库中运行良好。

model_attributes是最重要的,有什么变化,作为key-value格式的json字符串。

【讨论】:

以上是关于审计日志的数据库设计[关闭]的主要内容,如果未能解决你的问题,请参考以下文章

日志审计系统设计

日志审计功能实现

日志审计功能实现

日志审计功能实现

日志审计功能实现

Django 和 DRF 的审计日志