我目前正在考虑在我的数据库中为我的一些表创建历史表的想法。基本上,我有主表和该表的副本,其中包含修改日期和操作列,用于存储执行的操作,例如更新、删除和插入。
到目前为止,我可以想到三个不同的地方可以做历史表格工作。
- 在主表上触发更新、插入和删除。(数据库)
- 存储过程。(数据库)
- 应用层。(应用)
我的主要问题是;在每一层做这项工作的优点、缺点和陷阱是什么?
通过使用触发器方式,我能想到的一个优点是,无论在数据库之上实现什么,都始终保持完整性。
我目前正在考虑在我的数据库中为我的一些表创建历史表的想法。基本上,我有主表和该表的副本,其中包含修改日期和操作列,用于存储执行的操作,例如更新、删除和插入。
到目前为止,我可以想到三个不同的地方可以做历史表格工作。
我的主要问题是;在每一层做这项工作的优点、缺点和陷阱是什么?
通过使用触发器方式,我能想到的一个优点是,无论在数据库之上实现什么,都始终保持完整性。
我会这样说:
触发器是实现简单历史的最快和最简单的方法。以下信息假设了一个更复杂的示例,其中历史处理可能包括一些业务规则,并且可能需要在被跟踪的表中找不到的日志记录信息。
对于那些认为触发器比存储过程更安全的人,因为它们不能被绕过,我提醒他们他们正在做出以下假设:
!)存在阻止用户执行 DISABLE TRIGGER 的权限 [但是权限也可能存在以限制对数据库的所有访问,除了在 sprocs 上的 EXECUTE 这是企业应用程序的常见模式] - 因此必须假设正确的权限,因此 sprocs 相等在安全性和绕过能力方面的触发器
!)根据数据库,可能会执行不触发触发器的更新语句。我可以利用嵌套触发器执行深度的知识来绕过触发器。唯一确定的解决方案包括数据库的安全性和仅使用经批准的机制限制对数据的访问——无论这些机制是触发器、存储过程还是数据访问层。
我认为这里的选择很明确。如果多个应用程序正在访问数据,那么您希望从最低的公共层控制历史记录,这将意味着数据库。
按照上面的逻辑,触发器还是存储过程的选择又取决于存储过程是否是最底层的公共层。您应该更喜欢存储过程而不是触发器,因为您可以更好地控制性能和副作用,并且代码更易于维护。
触发器是可以接受的,但请尽量确保不会通过读取正在更新的表之外的数据来增加锁。将触发器限制为插入到日志表中,仅记录您需要的内容。
如果应用程序使用通用的逻辑访问层并且这不太可能随着时间的推移而改变,我更愿意在此处实现逻辑。使用责任链模式和插件架构,从依赖注入驱动它,以允许在你的历史模块中进行各种处理,包括记录到完全不同类型的技术、不同的数据库、历史服务或任何其他你可以想象。
多年来一直使用基于触发器的方法,它对我们来说确实效果很好,但是您确实有以下几点需要思考:
大量使用的触发器(例如,基于多租户 SaaS 的应用程序)可能非常昂贵
在某些情况下,一些字段可能会变得多余。只有当您对要记录的字段非常清楚时,触发器才是好的;尽管使用应用程序,您可以有一个拦截器层,它可以帮助您根据“配置”记录某些字段;尽管有自己的管理费用份额
如果没有足够的数据库控制,一个人可以很容易地禁用触发器、修改数据和启用触发器;一切都没有引起任何警报
对于 Web 应用程序,连接是从池中建立的,跟踪进行更改的实际用户可能很乏味。一个可能的解决方案是在每个事务表中都有“EditedBy”字段。
晚了一个,但它增加了几个可以考虑的选项。
更改数据捕获: 此功能在 SQL Server 2008 R2+ 中可用,但仅在企业版中可用。它允许您选择要跟踪的表,SQL Server 将为您完成这项工作。它通过读取事务日志和用数据填充历史表来工作。
读取事务日志:如果数据库处于完全恢复模式,则可以读取事务日志,并且可以找到几乎所有事务的详细信息。
缺点是默认情况下不支持此功能。选项是使用 fn_dblog 等未记录的函数或ApexSQL Log等第三方工具来读取事务日志。
触发器:适用于没有太多触发器需要管理的少量表。如果您有很多表要审核,那么您应该考虑使用一些第三方工具。
所有这些都在数据库级别工作,并且对应用程序完全透明。
触发器是捕获更改的唯一可靠方法。如果您在 Stored Procs 或应用程序中执行此操作,您可以随时进入并使用 SQL 删除您没有记录的更改(无意中)。当然,不想留下日志的人可以禁用触发器。但是您宁愿强迫某人禁用日志记录,也不愿希望他们记得包含它。
通常,如果您选择应用程序层,您可以设计您的应用程序代码以单点进行日志记录,这将一致地处理您所有的历史表。不同的触发器是一种更复杂的维护方法,因为它们(取决于数据库技术)为每个表复制:如果有数百个表,触发器的代码量可能是个问题。
如果您有一个支持组织来维护您现在正在编写的代码,并且您不知道谁将维护您的代码(对于大型行业来说很典型),您无法假设修复您的代码的人的技能水平应用程序,在这种情况下,我认为最好使历史表的工作原理尽可能简单,而应用程序层可能是实现此目的的最佳场所。