有效的审计方式
Posted
技术标签:
【中文标题】有效的审计方式【英文标题】:Efficient way for Auditing 【发布时间】:2020-08-04 05:36:19 【问题描述】:我正在尝试为我的 Spring Boot 应用程序实现一个审计层。到目前为止,我尝试了两种方法。
1) 创建了 1 个审计表,其中包含字段 user_name、table_name、column_name、old_value、new_value、uuid、event_type。
每当保存任何更改时,填充审计实体并保存它。
优点:
快速
易于管理,因为只有 1 个审计表
瀑布:
有时它涉及太多从业务实体到 审计实体
必须从数据库中获取旧值以填充审计实体
手动创建审计实体 检索可能会很痛苦2) 使用 javers 进行审计
优点:
审计实体的自动创建和更新
要管理的表数更少
不需要旧值检索
瀑布:
由于事务的大小很大,所以花费的时间太多基准测试
处理一个有 20 列(字段)的表(实体)中的 10 行,
使用方法 1 所用时间:24328 ms => 24 s
使用方法 2 所用时间:311292 ms => 311 s(近 12 次)
3) 没有使用 Hibernate 环境,因为创建的表数量会很高
有人可以提出一个更好的想法来进行上述优缺点的审计吗?我们的目标是,
要管理的表数量更少
响应时间更短
模块化审计层,自动化程度高于手动
每个表的列数为 10 到 25 列。
【问题讨论】:
哪个数据库提供者? 您是否尝试在低优先级的后台线程中运行审核?我将使用带有注释的 spring AOP,并在单独的后台线程中运行批处理的更新。该表的数据源也应该分开,这样审计就不会以任何方式修改应用程序的BU。 使用 Oracle 数据库 【参考方案1】:正如您所说,创建审计跟踪的常用方法是应用程序端库,如 envers 或 Javers。这些与持久性库挂钩,它们会维护数据表中的特定列(“createdBy”、“lastUpdated”等),和/或将早期记录版本复制到某种形式的历史表中。
不过,这也有一些缺点:
在历史表中写入记录作为 OLTP 事务的一部分会增加事务中执行语句的数量 -> 可能会导致应用程序的响应时间延长
支持批量更新和删除
无法跟踪直接在数据库中完成的更改
三步变更数据采集
-
捕获更改数据
将变更数据转换为目标数据库支持上传的格式
将数据上传到目标数据库
通过数据库触发器捕获更改数据
另一种技术是数据库触发器。无论是从应用程序还是数据库本身发出的,它们都不会错过任何操作。批量结单也将被处理。 基于触发器的 CDC 的另一个优点是应用程序不知道您添加了整个审计层这一事实。 不利的一面是,在将触发器作为 OLTP 事务的一部分执行时,仍然存在延迟增加的问题。
触发器的替代解决方案(此处为 Postgresql): 使用逻辑复制将数据库更改(解码的 WAL 消息)从 MASTER 服务器使用 WAL 流式传输到 SLAVE 服务器,并启用审核触发器以捕获对从服务器上复制表的更改。
通过基于日志的方法捕获更改数据
当利用transaction log
作为审计源并使用变更数据捕获来检索变更信息并将其发送到消息代理或基于持久日志(如Apache Kafka
)时,上述问题不存在。通常被认为是捕获变更数据的最佳方法,但不是最简单的设置解决方案。
以异步方式运行,CDC 进程可以提取更改数据,而不会影响 OLTP 事务。
只要有数据更改,就会在事务日志中添加一个条目。
在批量操作中更新或删除的每条记录都会有一个日志条目,因此可以为每个记录生成一个更改事件。
仍然是 CDC 如何访问元数据的问题,例如执行数据更改的应用程序用户、他们的 IP 地址、跟踪跨度 ID 或任何类型的相关 ID。
一种方法是使用一个单独的表来存储此元数据。应用程序可以为每个事务在该表中存储一个带有特定transactionId
的记录。数据更改事件将包含与更改相关联的 transactionID,因此可以将数据更改事件和元数据记录关联起来。
【讨论】:
【参考方案2】:我会采用这种方法:
1) 在低优先级的后台线程中审计
2) 使用带有注解的spring AOP将BU与AUDITING分开
3) 每次插入或更新实体时,将完整的 jsonized 对象写入带有 UID 的 NOSQL 数据库文档;旧值也没有用,因为您可以回溯运行简单查询的实体的更改
【讨论】:
【参考方案3】:显然你没有以正确的方式使用 Javers,你的数据不可靠。也许您正在为每个提交创建新的 Javers 实例?
Javers 与审计工具一样快。 Javers 只是为更改的对象创建快照并将它们插入数据库。每个快照一个数据库记录/文档。
因此,如果您更改一个对象,通常 Javers 会执行一次 DB 读取以获取其先前的快照,并执行一次 DB 插入以写入新快照。
【讨论】:
每次都会进行差异检查,以查看上一个快照与当前快照之间的变化。有什么方法可以忽略差异检查而只创建快照? 不,我使用了单例 javers 实例。 Javers 执行一次数据库读取以获取其先前的快照 - 有没有办法跳过这个? 不是一个好主意。数据审计是关于跟踪变化。如果您在没有差异检查的情况下提交 - 这将意味着写入大量重复的快照(并且性能下降,在大多数数据库中,写入比读取更昂贵)。 实际上我们为每个实体添加了最后更新时间。所以,diff 总是存在的。以上是关于有效的审计方式的主要内容,如果未能解决你的问题,请参考以下文章
T-SQL:创建四个连接表的聚合视图的有效方法(每个项目的每个审计类型的最大审计条目)