多实例并发下带LEFT JOIN的INSERT INTO SELECT防重失效求解
问题原因
当前跨表插入SQL在单实例下运行正常,但多实例并发调用时出现重复插入,核心原因是查询阶段没有对匹配范围加互斥锁:多个并发会话会同时读取到table2中尚未插入table1的同批次记录,各自拿到相同的结果集后执行插入,最终触发重复数据问题。
修正方案
1. 给查询语句加并发锁提示(SQL层面直接修复)
从语句使用的TOP语法判断你用的是SQL Server数据库,只需要在关联table1时加上UPDLOCK和HOLDLOCK表提示,就能保证并发场景下同批次待插入数据只会被一个会话读取到,修正后的SQL如下:
INSERT INTO table1 (col1, col2) SELECT DISTINCT TOP 10 t2.col1, t2.col2 FROM table2 t2 LEFT JOIN table1 t1 WITH (UPDLOCK, HOLDLOCK) ON t2.col1 = t1.col1 AND t2.col2 = t1.col2 WHERE t1.col1 IS NULL
锁提示的作用说明:
UPDLOCK:查询时对扫描到的匹配范围加更新锁,其余并发会话无法对同一范围数据加更新锁或排他锁,从根源避免多个会话读到同一份待插入数据HOLDLOCK:将锁持有到整个事务提交后再释放,不会在查询执行完就提前放锁,保证查询+插入的全流程互斥
2. 加唯一约束做兜底强保障
必须在table1上针对(col1, col2)字段创建唯一联合约束(或唯一联合索引),这是防止重复数据的最后一道防线:哪怕锁配置异常、或者有其他路径写入数据,数据库层面会直接拒绝重复值写入,不会产生脏数据。
创建唯一约束的示例SQL:
ALTER TABLE table1 ADD CONSTRAINT uk_table1_col1_col2 UNIQUE (col1, col2)
注意事项
- 执行上述插入SQL时建议显式开启事务,不要依赖自动提交,保证锁的生命周期符合预期
- 批量插入的
TOP值不要设置过大,避免锁范围过高影响业务并发吞吐量,可根据实际业务压测结果调整批量大小
内容的提问来源于stack exchange,提问作者Deepak Raj
相关产品推荐
相关产品推荐

