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

saf*_*rov 21 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)
...
Run Code Online (Sandbox Code Playgroud)

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

Kai*_*aii 23

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

顺便说一下,ORM映射器原则中有"可版本化"表数据的实现.在他们的文档中查看此示例.也许这符合您的需求,但它不符合我的要求.它似乎删除了原始记录被删除时的所有历史数据,使其不是真正的修订安全.

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

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

CREATE TABLE contact (
    id INT NOT NULL auto_increment,
    name VARCHAR(255),
    firstname VARCHAR(255),
    lastname VARCHAR(255),
    PRIMARY KEY (id)
)
Run Code Online (Sandbox Code Playgroud)

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

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)
)
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

优点:

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

优点:

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

如上所述,学说versionable做了类似的事情.


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

前言:不良做法,仅用于说明替代方案.

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

您有一个跟踪的中央历史记录表

  • 谁干的
  • 什么时候
  • 修改,插入或删除
  • 什么数据
  • 在哪个领域
  • 哪个表

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

优点:

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

优点:

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

结论:

  • 选项B对于小型应用程序来说非常方便,因为它只是用于记录更改时的简单"插入".
  • 如果您想回到过去并且能够轻松地比较历史修订版123与修订版125之间的差异和/或恢复到旧数据,那么选项A是很难的方法.