像在许多数据库中一样,我正在设计一个数据库,该数据库应记录每个表中更改的行的先前版本。
解决此问题的标准方法是为每个数据表保留一个历史记录表,
并且每当需要在数据表中更新一行时,就会将当前行的副本插入到历史表中,然后会更新数据表中的行。
此解决方案对我的缺点:
维护2个表而不是1个表(如果需要更改表的结构)应用程序需要知道两个表而不是一个 表的
名称可能需要短一些,以保持表名称和历史记录表名称的约定(例如,SOME_TABLE,SOME_TABLE_HIST)。
我正在考虑其他解决方案,并且想知道是否可以。
对于每个表,我们添加一列IS_LAST
当将一行插入表中时,它将以IS_LAST = 1插入。 当更新一行时,原始行的副本将被复制到同一表中,但更改为IS_LAST = 0,并且原始行将根据需要进行更新(仍保持IS_LAST = 1)。 假设在我的情况下,行平均更新10次。
还假设应用程序执行的操作中至少有90%仅在行的最新版本上发生。
我的数据库是Oracle 10g,因此为了使“事件”表保持精简,我们可以将表分为2个分区:IS_LAST = 1分区和IS_LAST = 0分区。
分区是解决历史数据保存问题的好方法吗?
此解决方案是否将其他分区潜力限制在这些表中?
谢谢!
第一个问题应该是:您将如何处理这些数据?如果您没有明确的业务要求,请不要这样做。
我做了类似的事情,运行3年后,大约有20%的“有效数据”,其余的是“以前的版本”。它是1000万+ 4000万条记录。在过去的三年中,我们有2(两个)请求来调查更改历史记录,并且两次请求都是愚蠢的-我们记录了记录更改的时间戳,并被要求检查人员是否加类(下午5点之后)。
现在,我们受困于超大型数据库,该数据库包含没有人需要的80%的数据。
编辑:
由于您要求提供可能的解决方案,因此我将介绍我们的工作。这与您正在考虑的解决方案有些不同。
所有表都具有代理主键。 所有主键均从单个序列生成。因为Oracle可以生成和缓存数字,所以效果很好,因此这里没有性能问题。我们使用ORM,我们希望内存中的每个对象(以及数据库中的对应记录)都具有唯一的标识符我们使用ORM,数据库表和类之间的映射信息以属性的形式出现。 我们将所有更改记录在单个存档表中,并包含以下几列:
id(代理主键)时间戳原始表 原始记录的
ID用户ID 事务类型(插入,更新,删除)将数据记录为varchar2字段
这是字段/值对形式的实际数据。 事情是这样工作的:
ORM具有插入/更新和删除命令。 我们为所有业务对象创建了一个基类,该基类覆盖了插入/更新和删除命令
insert/update/delete命令使用反射以字段名/值对的形式创建字符串。代码查找映射信息,并读取字段名称,关联的值和字段类型。然后,我们创建类似于JSON的内容(添加了一些修改)。创建表示对象当前状态的字符串时,会将其插入存档表。 当新对象或更新对象保存到数据库表中时,它保存到他的目标表中,同时我们将一个具有当前值的记录插入存档表中。 当对象被删除时,我们将其从目标表中删除,同时在归档表中插入一条交易类型为“DELETE”的记录优点:
我们没有数据库中每个表的存档表。我们也不必担心架构更改时更新存档表。 complete归档文件与“当前数据”分开,因此归档文件不会对数据库造成任何性能影响。我们将其放在单独磁盘上的单独表空间中,并且工作正常。 我们创建了2个表单来查看存档:
通用查看器,可以根据存档表上的过滤器列出存档表。用户可以在表单上输入过滤数据(时间跨度,用户等)。我们以字段名/值的形式显示每个记录,并且每个更改都用颜色编码。用户可以查看每个记录的所有版本,还可以查看更改的对象和时间。 发票查看器-这很复杂,但是我们创建了一个表单,该表单显示的发票与原始发票输入表单非常相似,但是带有一些其他按钮,可以显示不同的世代。创建此表单需要花费大量精力。表单使用了几次,然后被遗忘了,因为当前的工作流程中并不需要它。 用于创建存档记录的
代码位于单个C#类中。无需在数据库中的每个表上都使用触发器。 的性能非常好。在高峰时间,大约有700-800个用户使用该系统。这是ASP.Net应用程序。 ASP.Net和Oracle都在一个具有8Gb RAM的双XEON上运行。 缺点:
单表归档格式比每个数据表都有一个归档表的解决方案更难读。 在存档表中非id字段上搜索
很难-我们只能在字符串上使用LIKE
运算符。 因此,再次检查存档的要求。这不是一项艰巨的任务,但是收获和使用却很少。