在数据库设计中,删除数据通常有两种做法:逻辑删除和物理删除。逻辑删除并不真正把记录从表里去掉,而是用一个标记字段来表示这条数据已经失效;物理删除则是直接执行delete语句,把行从表中永久移除。这两种方式没有绝对的好坏,关键在于业务场景和系统诉求。

什么是逻辑删除
逻辑删除一般是在表中增加一个类似 is_deleted 的字段,删除时把它置为1,查询时过滤掉已删除的数据。
-- 增加逻辑删除字段 ALTER TABLE user ADD COLUMN is_deleted TINYINT NOT NULL DEFAULT 0; -- 逻辑删除某条用户 UPDATE user SET is_deleted = 1 WHERE id = 100; -- 查询时只取未删除的数据 SELECT id, name FROM user WHERE is_deleted = 0;
什么是物理删除
物理删除使用 delete 语句直接移除记录,数据不再占用表空间,也无法通过简单方式恢复。
-- 物理删除指定用户 DELETE FROM user WHERE id = 100;
两者的主要差异
| 对比维度 | 逻辑删除 | 物理删除 |
|---|---|---|
| 数据可恢复性 | 高,改标记即可恢复 | 低,需备份恢复 |
| 查询性能 | 稍差,需带过滤条件 | 较好,数据量更小 |
| 存储占用 | 已删数据仍占空间 | 立即释放空间 |
| 业务复杂度 | 每个查询都要处理标记 | 代码更简单 |
如何取舍
如果业务要求保留操作痕迹、满足审计或合规,比如订单、用户资料,优先使用逻辑删除。若数据是临时缓存、日志类且量非常大,物理删除更合适。也可以混合方案:核心表逻辑删除,流水表定期物理清理。
简单实践建议
- 用户、订单等核心数据用逻辑删除
- 会话、验证码等临时数据用物理删除
- 逻辑删除表要建好索引,避免查询变慢
删除方案不是一成不变的,随着数据量增长,要重新评估存储和性能成本。
代码示例:通用逻辑删除封装
// 使用MyBatis风格伪代码做逻辑删除
public void removeUser(long id) {
String sql = "UPDATE user SET is_deleted = 1 WHERE id = ?";
jdbcTemplate.update(sql, id);
}
// 查询时统一加条件
public User findUser(long id) {
String sql = "SELECT id, name FROM user WHERE id = ? AND is_deleted = 0";
return jdbcTemplate.queryForObject(sql, new UserRowMapper(), id);
}
总的来说,SQL逻辑删除与物理删除如何取舍,取决于你对数据安全、性能和业务清晰度的权衡。先想清楚数据是否允许丢失,再决定用哪种删除策略。