主键冲突的根本原因在于ID生成逻辑缺乏全局协调,触发器无法解决分布式并发写入下的重复ID问题;应优先采用UUID/ULID、Snowflake等全局ID服务,或应用层号段管理,仅在单主架构下谨慎使用触发器。

主键冲突通常不是触发器能解决的根本问题
分布式数据库里用触发器拦截 INSERT 并重写主键,看似可行,实则埋雷。触发器在单节点内生效,而分布式场景下多个节点并发写入时,BEFORE INSERT 触发器无法感知其他节点的未提交操作,照样会生成重复 id。你看到的“成功去重”,往往只是低并发下的侥幸。
真正该拦截的位置是应用层或ID生成服务
主键冲突的本质是ID生成逻辑脱离了全局协调。触发器既看不到跨节点状态,也无法阻塞其他节点的写入路径。实际可落地的做法包括:
- 用
UUID或ULID替代自增整数——写入前就确定唯一性,不依赖数据库生成 - 接入独立的
ID Generator服务(如 Twitter Snowflake、Leaf),由中心化服务分配带时间戳和节点标识的 long 型 ID - 若必须用数字主键,在应用层调用
SELECT FOR UPDATE锁定分段号池(例如每节点预取 1000 个id),但需承担锁争用和号段浪费成本
如果非要用触发器,只适合单点写入+多副本读场景
仅当你的“分布式”实际是主从架构(写请求全打到主库),且主库本身不横向扩展时,才可能用触发器兜底。即便如此,也得避开常见陷阱:
- 不要在触发器里查
MAX(id)+1——并发高时必然冲突,改用SEQUENCE(PostgreSQL)或GET_LOCK()(MySQL)做轻量互斥 - 避免在触发器中执行远程调用(如 HTTP 请求生成 ID),超时或失败会导致整个事务回滚不可控
- MySQL 的
BEFORE INSERT触发器不能修改AUTO_INCREMENT列值,必须显式赋值给新列或覆盖NEW.id
错误日志里出现 Duplicate entry '123' for key 'PRIMARY' 时别急着写触发器
先确认冲突来源:是应用重复提交?还是双写入口没收敛?或是分库分表规则错配导致同一逻辑ID落到不同物理表?触发器在这里只是遮羞布,掩盖了数据路由或幂等设计缺陷。真要加兜底逻辑,优先考虑在代理层(如 ShardingSphere、ProxySQL)拦截并重写 SQL,比数据库触发器更可控、可观测。
分布式系统里,主键不是靠补丁修出来的,是靠生成策略对齐拓扑结构设计出来的。越早把 ID 生成推到离业务最近、离存储最远的地方,后续踩的坑就越少。










