如何存储历史数据

有些同事和我就存储历史数据的最佳方式进行了辩论。 目前,对于某些系统,我使用一个单独的表来存储历史数据,并保留当前活动logging的原始表。 所以,假设我有桌子FOO。 在我的系统下,所有的活动logging都会以FOO的forms出现,所有的历史logging都会以FOO_Hist的forms出现。 FOO中的许多不同的领域可以由用户更新,所以我想保留一切更新的准确的帐户。 除了自动递增HIST_ID之外,FOO_Hist与FOO保持完全相同的字段。 每次更新FOO时,我都会在FOO_Hist中执行一条插入语句,类似于: insert into FOO_HIST select * from FOO where id = @id

我的同事说这是不好的devise,因为由于历史原因,我不应该有一个表的确切副本,应该插入另一个logging到活动表中,并带有一个标志,表明这是历史的目的。

是否有处理历史数据存储的标准? 在我看来,我不想把我所有的历史logging都logging在同一张桌子上,考虑到它可能超过一百万条logging(我想的是长期的)。

你或你的公司如何处理?

我正在使用MS SQL Server 2008,但我想保持答案通用和任意的任何DBMS。

直接在操作系统内支持历史数据将使您的应用程序比原本要复杂得多。 一般来说,我不会推荐这样做,除非你很难要求在系统中操作logging的历史版本。

如果仔细观察,历史数据的大部分要求可分为两类:

  • 审计日志logging:审计表更好。 编写一个工具可以非常容易地生成脚本,通过从系统数据字典中读取元数据来创build审计日志表和触发器。 这种types的工具可以用来改进对大多数系统的审计日志logging。 如果要实施数据仓库,也可以使用此子系统更改数据捕获(请参阅下文)。

  • 历史报告:报告历史状态,“现在”位置或分析报告。 通过查询上述types的审计日志表,可以满足简单的历史报告要求。 如果您有更复杂的要求,那么实施报告数据集市可能更经济,而不是尝试将历史直接集成到运营系统中。

    对于历史状态的跟踪和查询来说,缓慢变化的尺寸是迄今为止最简单的机制,并且许多历史跟踪可以被自动化。 通用处理程序并不难写。 通常,历史报告不必使用最新的数据,因此批量刷新机制通常很好。 这使您的核心和报告系统架构相对简单。

如果您的要求属于这两个类别之一,那么最好不要将历史数据存储在您的运营系统中。 将历史function分离到另一个子系统中可能会减less总体工作量,并产生事务性和审计/报告数据库,这些数据库对于其预期目的而言工作得更好。

我不认为有一个特定的标准的做法,但我想我会扔在一个可能的方法。 我在Oracle和我们的内部Web应用程序框架中工作,该框架使用XML来存储应用程序数据。

我们使用一种称为Master – Detail模型的东西,最简单的模型包括:

主表例如称为Widgets通常只包含一个ID。 经常会包含不会随时间变化的数据/不是历史数据。

细节/历史表例如称为Widget_Details至less包含:

  • ID – 主键。 详细资料/历史ID
  • MASTER_ID – 例如在这种情况下称为“WIDGET_ID”,这是主logging的FK
  • START_DATETIME – 表示该数据库行开始的时间戳
  • END_DATETIME – 表示该数据库行结束的时间戳
  • STATUS_CONTROL – 单个字符列表示该行的状态。 'C'表示当前,NULL或'A'将被历史/存档。 我们只使用这个,因为我们不能在END_DATETIME索引为NULL
  • CREATED_BY_WUA_ID – 存储导致该行创build的帐户的ID
  • XMLDATA – 存储实际的数据

所以基本上,一个实体开始时在主数据库中有1行,在细节中有1行。 具有NULL结束date和“C”的STATUS_CONTROL的细节。 发生更新时,当前行将更新为当前时间的END_DATETIME,并将status_control设置为NULL(如果需要,则为“A”)。 在详细信息表中创build了一个新行,仍然链接到同一主数据库,其中status_control为“C”,进行更新的人员的ID以及存储在XMLDATA列中的新数据。

这是我们历史模式的基础。 创build/更新逻辑是在Oracle PL / SQL包中处理的,因此您只需将该函数传递给当前标识,用户标识和新的XML数据,并在内部执行所有行的更新/插入操作以表示历史模型。 开始和结束时间表示表格中该行何时处于活动状态。

存储很便宜,我们通常不会删除数据,而更愿意保留审计线索。 这使我们能够在任何时候看到我们的数据是什么样的。 通过索引status_control ='C'或使用视图,混乱并不是一个问题。 显然你的查询需要考虑到你应该总是使用当前(NULL end_datetime和status_control ='C')版本的logging。

我认为你的做法是正确的。 历史表应该是没有索引的主表副本,请确保在表中也有更新时间戳。

如果您尽快尝试另一种方法,您将面临问题:

  • 维护开销
  • select更多的标志
  • 查询放缓
  • 表格,索引的增长

这个问题还比较老,但是人们仍然在这个问题上努力。 所以如果你使用oracle,你可能会对oracle闪回感兴趣: http : //docs.oracle.com/cd/B28359_01/appdev.111/b28424/adfns_flashback.htm

你可以把桌子分开吗?

“使用SQL Server 2008分区表和索引策略当数据库表增长到数百GB或更大时,加载新数据,删除旧数据和维护索引可能会变得更加困难,仅仅是表的大小导致这样的操作需要更长的时间,即使是必须加载或删除的数据也可能是非常大的,使得对表的INSERT和DELETE操作不切实际,Microsoft SQL Server 2008数据库软件提供了表分区,使这些操作更易于pipe理。

真正的问题是,您是否需要一起使用历史数据和活动数据进行报告? 如果是这样,将它们保存在一个表中,分区并创build活动logging的视图,以便在活动查询中使用。 如果你只需要偶尔看一下(研究法律问题或其他一些问题),然后把它们放在一个单独的表格中。

我知道这个老post,但只是想补充点。 这种问题的标准是最适合这种情况的。 了解这种存储的需求,以及潜在使用历史/审计/更改跟踪数据是非常重要的。

审计(安全目的) :为所有可审计的表使用公用表。 定义结构来存储列名称,值和值之后的字段。

存档/历史 :如跟踪以前的地址,电话号码等情况下创build一个单独的表FOO_HIST是更好的,如果你活跃的交易表模式未发生重大变化(如果你的历史表必须有相同的结构)。 如果您预期表格规范化,数据types更改添加/删除列,以xml格式存储您的历史数据。 使用以下列(ID,Date,Schema Version,XMLData)定义一个表。 这将很容易处理模式更改。 但你必须处理XML,这可能会引入一个复杂的数据检索水平。

您可以使用MSSQL服务器审核function。 从版本SQL Server 2012中,您将在所有版本中find此function:

http://technet.microsoft.com/en-us/library/cc280386.aspx

您可以在表格上创build物化/索引视图。 根据您的要求,您可以完整或部分更新视图。 请看这个创buildmview和日志。 如何在SQL Server中创build实体化视图?

只是想添加一个我开始使用的选项,因为我使用的是Azure SQL,而多表的事情对我来说太麻烦了。 我在我的表上添加了插入/更新/删除触发器,然后使用“FOR JSON AUTO”function将前/后更改转换为json。

  SET @beforeJson = (SELECT * FROM DELETED FOR JSON AUTO) SET @afterJson = (SELECT * FROM INSERTED FOR JSON AUTO) 

这会在更改之前/之后返回logging的JSON表示。 然后,我将这些值存储在一个历史logging表中,logging发生变化的时间戳(我也保存当前关注logging的ID)。 使用序列化过程,我可以控制如何在模式更改的情况下回填数据。

我从这里学到了这个链接

另一种select是将操作数据存档在[每小时|任何]基础上。 大多数数据库引擎都支持将数据提取到档案中 。

基本上,这个想法是创build一个预定的Windows或CRON作业

  1. 确定操作数据库中的当前表
  2. 将每个表中的所有数据select为CSV或XML文件
  3. 将导出的数据压缩为ZIP文件,最好使用文件名中的生成时间戳以便于归档。

许多SQL数据库引擎都带有一个可用于此目的的工具。 例如,在Linux上使用MySQL时,可以在CRON作业中使用以下命令来安排提取:

 mysqldump --all-databases --xml --lock-tables=false -ppassword | gzip -c | cat > /media/bak/servername-$(date +%Y-%m-%d)-mysql.xml.gz