软删除和硬删除怎么选?软删除表应该如何设计?
简化版
硬删除是直接删除物理记录,简单但不可恢复;软删除是用 deleted_at 或 is_deleted 标记记录已删除,便于恢复、审计和保留历史。软删除要处理唯一约束、查询默认过滤、数据膨胀、归档清理和误把删除数据查出来的问题。
详细版
软删除适合:
- 用户可恢复的数据;
- 有审计、合规、追踪要求;
- 其他表仍需要引用历史记录;
- 删除只是业务状态变化。
硬删除适合:
- 临时数据、缓存数据、中间表;
- 法规要求必须彻底删除;
- 数据没有历史价值;
- 已经归档且确认不再使用。
软删除字段常见设计:
deleted_at datetime null
deleted_by bigint null
delete_reason varchar(...)
相比 is_deleted,deleted_at 同时表达“是否删除”和“何时删除”,信息量更高。
完整版教学
一、删除不是一个纯技术动作
业务里的删除可能代表很多含义:
- 用户点了删除,但希望能恢复;
- 管理员下架内容;
- 数据过期进入归档;
- 法规要求用户注销后清除个人信息;
- 临时任务执行完可以清理。
这些语义不同,设计也不同。不要一上来就统一规定“都软删除”或“都硬删除”。
二、软删除的优点
软删除最大的好处是安全和可追溯:
- 误删可以恢复;
- 可以保留历史审计;
- 关联表仍能找到历史记录;
- 统计和追责更方便;
- 删除动作可以参与业务流程。
比如订单、合同、支付记录通常不应该随便硬删除。即使用户端看不到,后台审计和财务仍可能需要。
三、软删除的坑
软删除不是加个字段就完事。常见坑包括:
- 查询忘加
deleted_at is null,把已删除数据展示出来; - 唯一约束冲突,比如删除用户后无法重新注册同手机号;
- 表越来越大,影响索引和查询性能;
- 删除数据仍被统计进去;
- 关联关系复杂,父记录软删后子记录状态不清楚。
所以软删除要配套查询规范、索引设计和归档策略。
四、唯一约束怎么处理
假设用户表有唯一手机号。如果软删除后还希望手机号可重新注册,就不能只建:
unique(phone)
因为被软删除的记录仍在表里。不同数据库支持能力不同,可以考虑:
- 使用部分唯一索引,只约束未删除记录;
- 唯一键包含删除标记或删除时间;
- 删除时改写唯一字段,比如追加删除后缀;
- 业务上规定删除后手机号仍不可复用。
具体方案要结合数据库能力和业务规则,不能硬套。
五、什么时候要硬删除或归档
长期软删除会让主表膨胀。很多系统会采用两阶段:
- 先软删除,保留一段可恢复窗口;
- 到期后归档到历史表或做硬删除。
对于合规删除,比如用户要求删除个人信息,还可能需要脱敏、匿名化或物理删除。此时“为了方便恢复而一直软删除”可能不合规。
六、查询层要统一处理
软删除最好在数据访问层统一加条件,避免每个业务开发手写过滤。比如 ORM 全局条件、Repository 基类、SQL 模板规范。
但也要提供后台审计查询入口,让有权限的人能查已删除数据。软删除不是数据消失,而是普通业务视图不可见。
软删除还要区分“可恢复删除”和“合规删除”。前者强调误删恢复和审计,后者可能要求清除或匿名化个人信息。比如用户注销后订单可以保留财务记录,但收货人手机号可能需要脱敏,不能简单用 deleted_at 把明文数据永久留在主表。
七、常见误区与追问
| 删除策略 | 适合数据 | 注意事项 |
|---|---|---|
| 软删除 | 订单、合同、内容、可恢复数据 | 默认过滤、归档、唯一约束 |
| 硬删除 | 临时表、缓存表、无价值中间数据 | 删除前确认无审计和恢复需求 |
| 脱敏/匿名化 | 个人信息、合规删除 | 保留业务统计但移除身份识别 |
记忆钩子:软删除不是“数据删了”,而是“普通业务视图不可见”。只要记录还在库里,就要继续承担索引、权限、合规和清理成本。
唯一约束是软删除最常见的坑。假设用户 A 用手机号 13800000000 注册后软删除,如果表上仍是全局唯一:
unique(phone)
用户 B 或用户 A 重新注册同一手机号会失败。此时要明确业务规则:手机号删除后是否允许复用;允许复用时可用部分唯一索引、(phone, deleted_at) 组合策略或删除时改写唯一字段,不允许复用时则保留全局唯一。
- 误区:软删除就是加一个
is_deleted字段。 还要处理默认查询过滤、唯一约束、索引膨胀、归档、权限和审计。 - 误区:软删除比硬删除永远安全。 如果法规要求删除个人信息,一直软删除明文数据可能不合规。
- 误区:所有查询手写
deleted_at is null就够了。 手写容易漏,最好在数据访问层、ORM 或 Repository 规范中统一处理。 - 追问:为什么更推荐
deleted_at而不是单纯is_deleted?deleted_at同时表达是否删除和删除时间,便于恢复窗口、归档和审计。 - 追问:父记录软删后子记录怎么办? 要明确子记录是否仍可见、是否级联软删、是否禁止父记录删除,以及恢复时如何恢复关系。
- 追问:软删除数据越来越多怎么办? 设计两阶段生命周期:先软删除保留可恢复窗口,到期后归档、脱敏或硬删除。
八、加强记忆
硬删除简单彻底,软删除安全可恢复但会带来查询、唯一约束和数据膨胀问题。软删除建议用 deleted_at 表达删除时间,并配套默认过滤、唯一约束方案、归档清理和权限控制。