逻辑删除是我们唯一的选择吗?

Categories:

目前一提到逻辑删除,就是在表内增加一个字段,比如deleted,=1时表示已删除,=0时表示未删除。

但它也会带来一些问题,其中对我们日常开发影响较大的有

  1. 唯一索引无法使用。
  2. 大部分读操作(如果只是单表,自然没有问题;但对复杂报表、多表关联极其不友好)都需要过滤掉处于删除状态的记录。

难道除了逻辑删除,就没有其他的选择吗?

我们为什么需要逻辑删除

大多数情况下,逻辑删除的目的是备份,备份一定要在表内加deleted字段吗?

能不能在表外备份?类似回收站的概念。

让80%的操作都能直接简单粗暴的物理删除,另外的20%真的有需要备份的,我们底层去帮它做表外的备份。

比如我们建个trash回收站表,里面存 源表名、内容(大json)、删除人、删除时间。当调用delete时,先把这条记录存到trash表,然后在源表delete掉。

所以,实现一个【回收站】组件?

  1. 声明。在需要使用的表上加注解,如 @Trash

    @TableName("user")
    @Trash                             //在表这里加 @Trash 表示删除时要放入回收站
    public class User {
        private long id;
        private String name;
    }
    
  2. 识别或拦截。
    方案1:拦截delete操作,(较难拦截直接执行的delete sql语句?用sql parser等进行sql语法解析拦截所有的delete?)
    方案2:binlog等识别到delete后处理,引入一些binlog处理的中间件

  3. 处理。
    方案1:将要删除的记录复制到 trash(全局公用,存个大json字段,缺点是恢复较麻烦) 表后正常执行delete操作,将数据从源表里删除。
    方案2:项目启动时,当检测到entity上有@trash 时,自动建一个备份表。如果备份表已经存在,扫描并对比表结构,如果原始表和备份表结构不一样,自动计算一个alter table语句更新备份表的表结构。或者干脆手动维护备份表结构。

  4. 扩展。
    也许可以同时维护一个回收站管理?
    比如回收站默认存30天,30天后就真正删除(定时任务)。
    比如提供一个管理界面,给(用户/管理员)恢复误删的记录。

另外一个方案:用视图+逻辑删除代替物理表?

假设有个User entity,映射的表名为 user,加了 @Trash 注解。
启动的时候,自动将表重命名为 t_user,然后新建一个视图 user。
其中视图 user的逻辑是 select * from t_user where deleted=0
在操作表的时候,就拿这个视图当物理表来用。
PS:简单的单表视图是可以直接操作 insert update delete 的(没有一些group等特殊过滤的、具体限制这里不列出来)

写在最后

以上纯属脑洞,未经实际验证,纯属抛砖引玉。

另外,在实际开发中,很多人把删除冻结的概念混淆了,请参看文章逻辑删除和冻结就不是一回事

如果你觉得本文对你有帮助或不错,可略表心意,请我喝一杯冰可乐。

Comments