哈希不能直接作主键,因其存在碰撞概率,而数据库唯一约束要求绝对唯一;正确做法是哈希生成short_code并设为NOT NULL+UNIQUE索引,主键仍用自增id,两者解耦。

为什么不能只靠哈希值做主键
短链系统里,常见做法是用 URL 哈希(比如 MD5、xxHash)截取前 6–8 位作为短码,存进 short_code 字段。但哈希再强也有碰撞概率——哪怕极低,一旦两条不同 URL 算出相同短码,INSERT 就会触发 ERROR 1062。如果直接把哈希结果当主键或唯一键,冲突时只能报错或覆盖,业务上不可控。
核心问题在于:哈希是“尽力而为”的映射,而数据库约束要求“绝对唯一”。所以必须分层:哈希负责快速路由和用户可见短码,MySQL 自增主键负责底层可靠标识,两者不耦合。
唯一索引必须建在 short_code 上,且允许 NULL?
不是。唯一索引要建在 short_code 上,但不能允许 NULL——否则多条记录的 short_code IS NULL 会被视为非重复(MySQL 对 NULL 的唯一性校验是宽松的),破坏语义。正确做法是:
-
short_code设为NOT NULL+UNIQUE - 表主键仍用自增
id,不参与业务逻辑 - 插入时始终显式提供
short_code值,绝不留空
这样,哈希冲突时 MySQL 在唯一索引层就拦截,不会走到主键层。错误信息会明确指向 short_code 索引,而不是含糊的 PRIMARY,便于定位。
INSERT IGNORE 和 ON DUPLICATE KEY UPDATE 怎么选
两者都依赖 short_code 上的唯一索引,但行为差异直接影响短链语义:
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
-
INSERT IGNORE INTO links (short_code, url) VALUES ('abc123', 'https://...');→ 冲突时静默跳过,返回影响行数 0,适合“只创建新短链,旧的不管”场景 -
INSERT INTO links (short_code, url) VALUES ('abc123', 'https://...') ON DUPLICATE KEY UPDATE url = VALUES(url), updated_at = NOW();→ 冲突时更新原记录,适合“强制绑定该短码到最新 URL”场景(如运营后台重定向配置)
注意:REPLACE INTO 不推荐——它本质是 DELETE + INSERT,会改变 id 值,导致外部引用失效,且可能触发外键级联或触发器意外行为。
自增 ID 跳变和 AUTO_INCREMENT 错位怎么稳住
用 INSERT IGNORE 或 ON DUPLICATE KEY UPDATE 时,MySQL 仍会预分配自增 ID(即使最终没插入成功),导致 AUTO_INCREMENT 值不断上涨,ID 不连续。这不是 bug,是 InnoDB 为并发安全做的取舍。
只要不依赖 ID 连续性(短链系统完全不依赖),这没问题。但要注意两个坑:
- mysqldump 导出后恢复,若没执行末尾的
ALTER TABLE ... AUTO_INCREMENT = N,新库的自增值会从 1 开始,立刻撞1062 - 主从复制中,若未配对设置
auto_increment_increment和auto_increment_offset,双写场景下自增 ID 会真实重复
真正关键的是:短码的唯一性由 UNIQUE 索引保证,ID 只是内部代理。别试图用 ID 推算短码,也别让业务逻辑感知 ID 是否“跳跃”。

















