存储所有数据更改与每个细节(如Stackoverflow)

时间:2012-03-24 14:58:17

标签: php mysql revision-history

我使用Codeigniter编写系统,使用MySQL编写数据库。系统具有用户,具有不同权限的用户组等。拥有许多具有多对多关系的mysql表。

我有一些表:

  • 合同
  • 客户
  • 产品
  • product_features
  • 订单
  • order_features
  • order_products
  • 等...

目前,我正在记录用户对这些表的数据所做的每一次更改。用户可以根据其权限更改这些数据。存储日志更改只是简单的形式,如

A user changed product features with id of A8767
B user added new customer with id 56
C user edited content of orderlist
A user added new product (id: A8767) to order (id: or67)
...

我希望保留所有针对每个细节所做的更改,例如编辑Stackoverflow问题的历史记录。我可以考虑log_table设计来保持各种表的所有数据更改。有没有办法,教程,引擎,插件来做到这一点?只有我能想到制作每张桌子的副本并继续存储它们的变化,但我不认为它的好方法。

2 个答案:

答案 0 :(得分:21)

我现在已经考虑了一段时间,只能想到两种方法。当制作成抽象数据层/模型时,两者都可以完全透明。

顺便说一句,有一个"版本化的实现" ORM映射器原则中的表数据。见example in their docs。也许这符合您的需求,但它并不适合我的需求。它似乎删除了原始记录被删除时的所有历史数据,使其不是真正的修订安全。

选项A:拥有每个表的副本以保存修订数据

假设您有一个简单的联系表:

CREATE TABLE contact (
    id INT NOT NULL auto_increment,
    name VARCHAR(255),
    firstname VARCHAR(255),
    lastname VARCHAR(255),
    PRIMARY KEY (id)
)

您将创建该表的副本并添加修订数据:

CREATE TABLE contact_revisions (
    id INT NOT NULL,
    name VARCHAR(255),
    firstname VARCHAR(255),
    lastname VARCHAR(255),
    revision_id INT auto_increment,
    type ENUM('INSERT', 'UPDATE', 'DELETE') NOT NULL,
    change_time DEFAULT current_timestamp,
    PRIMARY KEY(revision_id)
)

使用INSERT触发器跟踪UPDATEAFTER。在原始数据的每个新数据修订版中,在修订表中插入新数据的副本,并正确设置修改type

要记录DELETE版本安全,您还必须在历史记录表中插入新行!为此,您应该使用BEFORE DELETE触发器并在删除之前存储最新值。否则,您还必须删除历史记录表中的每个NOT NULL约束。

有关此实施的一些重要说明

  • 对于历史记录表,您必须从修订表中删除每个UNIQUE KEY(此处:PRIMARY KEY),因为每次数据修订都会多次使用相同的密钥。
  • 当您通过更新(例如软件更新)ALTER原始表中的架构和数据时,您必须确保将相同的数据或架构更正应用于历史记录表及其数据。否则,在恢复到较旧版本的记录集时,您将遇到麻烦。
  • 在现实世界中,您可能想知道哪个用户修改了数据。要使该版本安全,应永远不要从users表中删除用户记录。您应该只使用标志设置帐户。
  • 通常,单个用户操作涉及多个表。在实际的实现中,您还必须跟踪多个表中的哪些更改属于单个用户事务以及哪个顺序。在实际用例中,您可能希望以相反的顺序将单个事务的所有更改还原在一起。这需要一个额外的修订表来跟踪用户和事务,并与历史表中的所有个别修订保持松散的关系。

优点:

  • 完全在数据库中,独立于应用程序代码。 (好吧,当跟踪用户事务很重要时,这不需要在单个查询范围之外的某些逻辑)
  • 所有数据都采用原始格式,没有隐式类型转换。
  • 修订版中的搜索效果良好
  • 轻松回滚。只需在原始表上执行一个简单的INSERT .. ON DUPLICATE KEY UPDATE ..语句,使用要回滚的修订版本中的数据。

优点:

  • 难以手动实施。
  • 在数据库迁移/应用程序更新方面很难(但并非不可能)自动化。

如上所述,doctrines versionable做了类似的事情。


选项B:有一个中央更改日志表

前言:不良做法,仅为替代例示。

这种方法很大程度上依赖于应用程序逻辑,应该隐藏在数据层/模型中。

您有一个跟踪

的中央历史记录表
  • 谁做了
  • 修改,插入或删除
  • 什么数据
  • 在哪个字段
  • 哪个表

与其他方法一样,您可能还希望跟踪哪些单个数据更改属于单个用户操作/事务以及顺序。

优点:

  • 在向表中添加字段或创建新表时,无需与原始表保持同步。它透明地扩展。

优点:

  • 使用数据库中的简单值=密钥库
  • 的不良做法
  • 由于隐式类型转换而导致搜索性能下降
  • 当中央历史表由于写锁而成为瓶颈时,
  • 可能会降低应用程序/数据库的整体性能(这仅适用于具有表锁的特定引擎,即MyISAM)
  • 实施回滚要困难得多
  • 由于隐式类型转换而可能导致的数据转换错误/精度损失
  • 当您在代码中的某个位置直接访问数据库而不是使用模型/数据层时,
  • 不会跟踪更改,并且忘记在这种情况下您必须手动写入修订日志。与其他程序员一起工作时可能是个大问题。

<强>结论:

  • 选项B 对于小型应用来说非常方便,因为它可以简单地放入&#34;当它只是为了记录变化。
  • 如果您想回到过去,并且能够轻松比较历史修订 123 与修订 125 之间的差异和/或恢复旧数据,那么选项A 是艰难的选择。

答案 1 :(得分:2)

使用一般的uni更新表怎么样?表字段应包含以下值:

user,event,date,table,field,new value

  • 用户 - 谁进行了更改
  • event - 作为预定义事件的代码(更新,保存,插入)
  • 日期 - 更改时
  • table&amp;字段 - 可以从全局查询中自动本地化
  • value - 插入值

可以使用常规查询中的某些函数创建值和插入。