167

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

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

我还偶然发现了这篇关于维护数据库更改日志的有趣文章,它试图列出每种方法的优缺点。它写得很好,信息也很有趣,但它让我的决定更加困难。

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

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

我知道的方法是:

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

表格示例:

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

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

2. 只插入表格

表格示例

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

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

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

历史表示例:

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

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

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

历史表示例:

  • 表名
  • 场地
  • 用户
  • 新值
  • 已删除(布尔值)
  • 时间戳

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

4

6 回答 6

93

一些 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

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

于 2010-01-07T12:55:30.560 回答
14

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

于 2010-01-06T18:47:01.013 回答
7

我不知道任何参考,但我确定有人写过一些东西。

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

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

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

于 2010-01-06T18:34:13.670 回答
5

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

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表。所有列都被索引,并且为引用 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;
于 2015-04-24T05:25:54.473 回答
3

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

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

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

于 2010-01-06T18:47:56.567 回答
1

我正在使用以下结构:

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 是最重要的,改变了什么,作为键值格式的 json 字符串。

于 2021-04-13T20:47:40.813 回答