使用TABLOCK向非分区CCI并行插入时出现ROWGROUP_FLUSH死锁咨询
问题1:移除TABLOCK后不再出现死锁且仍支持并行插入的原理
你参考的旧版官方规则适用于SQL Server 2016及更早版本,从SQL Server 2019(对应你使用的兼容级别150)开始,数据库引擎放宽了聚集列存储索引(CCI)并行插入的限制:只要优化器判断待插入数据量足够大、并行执行收益更高,即使不加TABLOCK提示也会生成并行插入执行计划,这就是你移除TABLOCK后仍能并行插入的核心原因。
而死锁消失的原因和两种模式下的行组分配逻辑直接相关:
- 加TABLOCK时,CCI会启用最小日志批量加载模式,多个并行线程会统一申请全局的压缩行组配额,线程之间在刷新行组、更新CCI元数据时会互相争用
ROWGROUP_FLUSH类型的排他锁,你遇到的就是同会话下多个并行线程循环申请锁导致的自身死锁。 - 移除TABLOCK后,并行线程不需要争用全局的行组分配锁,每个线程独立处理自己的数据集,锁的粒度更小,不会出现循环等待的场景,因此死锁不再触发。
问题2:移除TABLOCK提示是否会引发其他问题
移除TABLOCK主要会带来两点潜在影响,你可以结合自己的业务场景判断:
- 存储开销和后台负载:不带TABLOCK插入时,如果单线程单次写入的行数不足102400行,数据会先写入行存储格式的delta开放行组,后续需要等待后台tuple mover进程将其压缩为列存储格式的闭合行组,会产生额外的后台资源开销。如果插入数据有较多小批次的零散数据,还会出现大量小行组,降低CCI的查询性能。如果你的场景是1000万行的单批次插入,这个影响很小,也可以在插入完成后手动执行
ALTER INDEX cci_Data_MostRecent ON dbo.Data_MostRecent REORGANIZE强制压缩所有delta行组。 - 并发写入冲突:TABLOCK会持有整个表的排他锁,插入过程中不会有其他写入操作冲突;如果移除TABLOCK,同时有其他会话向该表写入数据,可能出现锁争用甚至写入失败的问题。如果你的场景是定期独占式全量刷新该表,没有其他并发写入,这个风险不存在。
针对你当前的场景(插入前先TRUNCATE表、单事务批量写入、无其他并发写入),移除TABLOCK不会带来明显的业务问题,也可以满足并行插入的需求。
内容的提问来源于stack exchange,提问作者Avyncentia
相关产品推荐
相关产品推荐

