1. 什么是软删除?

软删(soft delete)除是相对于常见的数据库删除命令 DELETE 来说的. 执行 DELETE 命令数据库表中的记录会被真实的删除(hard delete), 在某些场景下希望数据不要被删除比如数据有分析和统计的意义又不需要在前台业务上呈现, 这个时候一般有两种实现方式:

  • 软删除: 在数据库表中增加一列例如: deletedAt, 需要删除的时候使用 UPDATE 替换 DELETE 命令, 修改 deletedAt 为一个不为 NULL 的值, 一般是时间戳. 这种删除方式也称为标记删除.
  • 删除迁移: 创建一个和生产库相同表结构专门为删除数据准备的数据库, 或者直接在生产库中创建为一套需要删除数据表对应的删除表, 当执行 DELETE 的时候可以将要删除的数据从生产的表中转移到删除表中, 然后在生产库的表执行删除操作.

简单比较一下软删除和迁移删除, 软删除操作比较简单且成本相对较小, 删除迁移相对比较复杂还需要考虑到业务表和删除表两个操作的事务性, 除此之外迁移在有外键约束的情况下, 迁移数据的逻辑也变的更复杂, 所以一般在设计删除的时候都采用软删除.

2. 软删除是否完美?

最近因为业务需要, 我们进行了一次讨论关于软删除和硬删除, 我也做了一些软删除的测试和实践, 首先分享一下我们讨论的一个观点: 不管是软删除还是硬删除都是删除, 既然是删除行为应该一致. 具体可以从以下两点理解这句话:

  1. 硬删除删除了就查不来了, 软删除删除了也应该查不出来(不做特殊处理查不出来)
  2. 硬删除会级联删除, 软删除应该也自动标记级联的实体的实例删除

上面的观点1如果想要达成, 如果不通过 ORM 的话很难从纯 SQL 上解决, 我们假设都使用 ORM, 那么测试的核心就是: 在各种查询下不做特殊指定 ORM 总是返回未被软删除的数据. 上面观点2的测试重点也很简单: 如果级联删除, 关联表记录也被软删除.

因为是已有业务和已有的技术栈, 这里我测试是 nodejs 的一个 ORM: Sequelize, 数据库则是: mysql.
测试过程忽略, 测试结果:

  1. ORM 可以在不做特殊指定的时候默认不查询已经软删除的数据记录, 这个点保证了我们已有的业务代码修改不会太多.
  2. 软删除场景下 ORM 不能做到级联删除, 这点也很好理解, 级联删除是数据库引擎的事情, ORM 不太好去实现, 所以如果级联情况下需手工处理.

至此, 基本了解了软删除的可行性. 而从技术角度查看执行 SQL 可以发现软删除表在不做特殊指定的时查询和更新操作默认都会增加一个筛选条件:

1
(deletedAt is null or deletedAt < now())

所以 ORM 通过默认增加的筛选条件实现了观点1.
如果总结一下: 在技术角度有 ORM 配合的软删除还是很不错的.

3. 软删除与业务结合时需要注意的坑

单从技术上软删除可行性还是挺不错的, 如果结合业务就是另外一回事情了, 简单总结一下软删除结合业务时候需要注意的点:

  • unique index: 如果表有唯一索引, 比如用户表的邮箱列有唯一索引, 那么在做了软删除后被删除行的邮箱从数据角度任然在起作用, 所以不能创建相同邮箱的用户.
  • performance: 上小节最后发现 ORM 是通过增加一个由 or 组成的附加条件来实现被删除的数据不被使用, 这时候需要考虑 SQL 的执行效率, 可以给标记列创建索引.

4. 参考资料(Sequelize)