如何设计一个支持某个功能的数据库,该功能允许应用程序用户在某个时间点创建数据快照,有点像版本控制。
这将使用户能够返回并查看他们过去的数据。
假设“快照”的数据很复杂,并且包含多个表的连接。
我正在寻找一种方法,让每个应用程序用户能够对其数据进行快照并返回到它。整个数据库快照不是我想要的。
编辑:谢谢你的回答。 6NF的答案非常引人注目,因为它简化了快照数据的去规范化建议。
澄清:这不是数据仓库问题,也不是关于数据库备份和恢复的问题;它是关于如何构建一个模式,允许我们在某个时间点捕获特定的一组相关数据的状态。应用程序用户在认为合适时生成快照。用户不会对整个数据库进行快照,只会对他们感兴趣的数据对象进行快照。
答案 0 :(得分:18)
这并不容易。
你基本上要求一个时间数据库(Christopher Date称之为第六范式,或6NF)。
要成为6NF,模式也必须是5NF,和,基本上,对于每个数据,您需要附加该值的数据适用的时间范围。然后在连接中,连接必须仅包括在所考虑的时间范围内的行。
时间建模很难 - 它是第6个普通表格地址 - 并且在当前的RDBMS中得不到很好的支持。
问题在于粒度。第6范式(我理解)支持时间建模,通过使每个非密钥(非密钥:,即,在实体上“可以在没有实体失去其身份的情况下改变”的任何东西)成为单独的关系。为此,您可以添加时间戳或时间范围或版本号。使所有内容成为连接可以解决粒度问题,但这也意味着您的查询将变得更加复杂和缓慢。它还需要确定所有键和非键属性;这往往是一项巨大的努力。
基本上,无论你在哪里都有关系(“泰德拥有通用股票证书,身份证号为789”),你都会增加一个时间:“泰德拥有通用股票证书,身份证号为789 现在”,这样你就可以了可以同时说,“fred拥有从2000年2月3日到昨天的id为789的GM股票证书”。显然这些关系是多对多的,(ted现在可以拥有多个证书,并且在他的一生中也可以拥有多个证书,而且fred现在可以拥有证书杰克拥有的证书)。
因此,我们有一个所有者表,一个股票证书表和一个多对多表,它通过id关联所有者和证书。对于多对多表,我们添加了一个start_date和一个end_date。
现在,想象一下每个州/省/土地对股票的股息征税,因此为了税收目的,记录股票证书所有者的居住状态。
拥有者居住的地方显然可以独立改变股权;特德可以住在内布拉斯加州,购买10股,获得内布拉斯加州的股息,转移到内华达州,向弗雷德出售5股,再买10股。
但是对于我们来说,它可以在某个时间转移到内布拉斯加州,在某个时间购买10股,,在某个时间获得红利 >,内布拉斯加州的税收,在某个时间转移到Neveda ,在某个时间出售5股给fred ,在某个时间再购买10股
如果我们想要计算内布拉斯加州和内华达州所欠的税款,加入person_stockcertificate和person_address中的匹配/重叠日期范围,我们需要所有这些。一个人的地址不再是一对一的,它是一对多的,因为它的地址在时间范围内。
如果特德买了十股,我们是否用一个购买日期对买入事件进行建模,或者我们是否为每个股票添加了date_bought?取决于我们需要模型回答的问题。
答案 1 :(得分:10)
我们通过创建包含我们想要快照的数据的单独数据库表进行了一次,但是非规范化,即每个记录包含有意义所需的所有数据,而不是对可能或可能不再存在的id的引用。它还为每一行添加了一个日期。
然后,我们为在所有受影响的表上执行连接的特定插入或更新生成触发器,并将其插入到快照表中。
通过这种方式编写将用户数据恢复到某个时间点的内容是微不足道的。
如果你有一张桌子:
用户:
id, firstname, lastname, department_id
系:
id, name, departmenthead_id
用户表的快照可能如下所示:
user_id, user_firstname, user_lastname, department_id, department_name, deparmenthead_id, deparmenthead_firstname, departmenthead_lastname, snapshot_date
和类似
的查询INSERT INTO usersnapshot
SELECT user.id AS user_id, user.firstname AS user_firstname, user.lastname AS user_lastname,
department.id AS department_id, department.name AS department_name
departmenthead.id AS departmenthead_id, departmenthead.firstname AS departmenthead_firstname, departmenthead.lastname AS departmenthead_lastname,
GETDATE() AS snapshot_date
FROM user
INNER JOIN department ON user.department_id = department.id
INNER JOIN user departmenthead ON department.departmenthead_id = departmenthead.id
这确保快照中的每一行在该时刻都为真,即使部门或部门负责人在此期间发生了变化。
答案 2 :(得分:1)
拥有快照和/或审计跟踪是常见的数据库要求。对于许多应用程序,创建“影子”或审计表是一项简单而直接的任务。虽然数据库级备份和事务日志很好,但它们不是版本控制系统。
基本上,您需要创建一个包含与基表完全相同的列的影子表,然后在基表上设置触发器,以便在更新或删除时将该行的副本放在影子表中。
通过某种逻辑,您可以重新创建在给定时间点数据的样子。有关在Sybase中进行此设置的简便方法,请参阅:http://www.theeggeadventure.com/wikimedia/index.php/Sybase_Tips#create_.27audit.27_columns
如果您需要执行大量历史快照,则可以将数据保存在同一个表中。基本上,创建两列 - 添加和删除列。缺点是每个查询都必须添加where子句。当然,您可以创建一个只显示活动记录的视图。如果您有一个带有多个表的规范化数据库,这会有一些历史记录,这会变得有点复杂。
然而,它确实有效。您只需在每个表上添加“已添加”和“已删除”列,然后您的查询就会显示您感兴趣的时间点。每当修改数据时,您必须复制当前行,并将其标记为已删除。
答案 3 :(得分:1)
捕获所有数据更改,使其能够像在任何时间点一样进行查询。
答案 4 :(得分:0)
SQL Server 2005(以后版本)企业版可以创建Database snapshots
答案 5 :(得分:0)
版本9i的Oracle具有闪回技术,在Oracle 10g和11g中得到了很大改进,如果启用闪回,您可以在历史记录的任何给定点查看数据库的状态。
查看此文档:Flashback Overview
答案 6 :(得分:0)
您可以使用RDBMS生成的日志来获取数据的快照。通常,日志用于提供数据库恢复。但是,它们也可用于跨多个RDBMS实例复制数据或获取数据的快照。
要获取数据的快照,只需考虑在所需时刻之前生成的所有日志。然后,您“回放”这些日志以获取已恢复数据的实际数据库。
如何访问和“回放”日志取决于您使用的具体RDBMS产品。
另一种可能性是使用时态数据库。它们具有内置的时间方面,并允许“回到过去”。例如,查找“Oracle闪回技术”。 http://www.stanford.edu/dept/itss/docs/oracle/10g/appdev.101/b10795/adfns_fl.htm#ADFNS1008
答案 7 :(得分:0)
至少使用SQL Server,您可以使用完整日志记录并在每个备份集之间保留事务日志。
然后你可以做一个时间点备份。
这是一个糟糕的解决方案。
您的客户究竟想要什么?是用于分析目的(即问题就像两周前我们有多少订单)?因为这正是数据仓库解决的问题。
答案 8 :(得分:0)
也许可以考虑使用像MongoDB这样的NoSql解决方案将所有关系数据聚合到单个文档中,然后使用时间戳或版本号存储该文档。像Kafka-Connect或Oracle Golden Gate这样的解决方案可以简化管道关系数据到NoSql商店。
答案 9 :(得分:0)
无论是主表还是事实/事务表,我都会在每个表上使用一个额外的时间戳字段。而且,即使目标是更新,也需要重新插入所有表。