MySQL 联合唯一索引(code, deleted)与逻辑问题
问题背景
在使用逻辑删除场景时,开发者通常会添加联合唯一索引来保证业务字段在全生命周期内唯一。但若联合索引包含 deleted 字段(值为 0/1),在物理删除后重新插入同名记录时会报 Duplicate entry 错误。
原因分析
- 逻辑删除的本质 :标记
deleted = 1而非物理移除记录。 - 联合唯一索引约束 :
(code, deleted)唯一要求 code + deleted 组合不可重复。 - 物理删除后重插冲突:当记录被真正物理删除后,业务上同一 code 本可以重用,但因历史唯一约束仍可能被拦截(取决于索引策略与并发场景)。
解决方案
方案一:业务层判断 + 唯一索引排除删除行
sql
-- 不将 deleted 纳入唯一索引
ALTER TABLE table_name DROP INDEX uk_code_deleted;
ALTER TABLE table_name ADD UNIQUE INDEX uk_code(code);
-- 业务层用 SELECT ... WHERE deleted = 0 AND code = ? 做判空校验
方案二:软删除 + 唯一索引包含 deleted,但需物理删除后释放
sql
-- 索引保持 (code, deleted)
-- 真正删除时通过 DELETE 释放该索引占用
-- 重新插入时使用 DELETE FROM table_name WHERE deleted = 1 AND code = ? 清理再 INSERT
方案三:使用唯一索引 + 逻辑标记组合,通过唯一索引约束 + 定时清理
- 保留索引但不依赖
deleted,单独设置定时任务物理清理已删除记录。
推荐做法
推荐方案一:唯一索引只覆盖业务字段(如 code),逻辑删除状态由 WHERE 条件过滤,业务校验层确保唯一性。
总结
逻辑删除 + 唯一索引设计需要谨慎规划,确保删除-恢复-重用的业务链路顺畅。