SQL Server MERGE 语句在并发场景下的锁升级问题
发布时间 - 2026-01-31 00:00:00 点击率:次MERGE会触发锁升级而非仅行锁,因其默认对目标表扫描范围加意向锁(IX),当预估或实际处理行数超阈值(约5000行)时,SQL Server自动将行锁升级为页锁(PAG)或表锁(TAB);即使只更新一行,若扫描6000行才定位到,仍可能升级。
为什么 MERGE 会触发锁升级而不是行锁
MERGE 在执行时默认对目标表(target_table)的扫描范围加意向锁(IX),如果优化器预估要处理的行数较多,或实际执行中触发了锁数量阈值(通常是 5000 行左右),SQL Server 会自动将大量行锁升级为页锁(PAG)甚至表锁(TAB)。这不是语句写法问题,而是锁管理器的主动行为——哪怕你只 UPDATE 一行,只要扫描了 6000 行才找到它,就可能被升级。
常见现象包括:其他会话对同一表的 INSERT 或 UPDATE 被长时间阻塞,sys.dm_tran_locks 中看到大量 PAG 或 TAB 类型锁,且 resource_description 显示为整页或全表。
- 确保
ON条件列有高效索引(最好是唯一索引或主键),避免全表扫描 - 避免在
USING子句中使用未索引的视图、函数表达式或计算列作为匹配依据 - 用
OPTION (LOOP JOIN)强制嵌套循环连接,可限制扫描范围(但需确认驱动表足够小)
MERGE 的 WHEN MATCHED 和 WHEN NOT MATCHED 实际加锁差异
WHEN MATCHED 执行 UPDATE 时,SQL Server 会对匹配到的**每一行先加 U 锁(更新锁)再升级为 X 锁**;而 WHEN NOT MATCHED 执行 INSERT 时,会在插入位置加 IX + X 锁,但若聚集索引不连续(如 GUID 主键),可能引发页分裂并扩大锁影响范围。
关键点在于:MERGE 不是原子性地“先查再定操作”,而是在连接阶段就锁定所有潜在参与匹配的行。这意味着即使某行最终走的是 INSERT 分支,它在 ON 匹配扫描过程中已被锁住。
- 不要在
WHEN MATCHED中更新非索引列以外的大字段(如varchar(max)),会延长 X 锁持有时间 - 若业务允许,把
INSERT拆出来单独做(用NOT EXISTS+INSERT),可绕过 MERGE 的批量扫描锁 - 测试时用
DBCC TRACEO查看实际锁申请序列(生产环境慎用)
N(1200, -1)
如何验证当前 MERGE 是否已发生锁升级
直接查动态管理视图比看执行计划更可靠。执行 MERGE 后立即运行:
SELECT resource_type, resource_description,
request_mode, request_status, COUNT(*) cnt
FROM sys.dm_tran_locks
WHERE resource_database_id = DB_ID()
AND resource_associated_entity_id = OBJECT_ID('your_target_table')
GROUP BY resource_type, resource_description, request_mode, request_status;
如果结果中 resource_type 出现大量 PAG 或 TAB,且 request_mode 是 X 或 U,基本确认发生了锁升级。注意:KEY 类型表示行锁(正常),OBJECT 表示整个堆/索引被锁(危险信号)。
- 检查
sys.dm_exec_requests中该会话的wait_type,常见如LCK_M_X长时间等待说明锁冲突严重 - 对比加索引前后
resource_description中的页号(如(1:123456))是否从分散变集中 - 避免依赖 SSMS 的“包含实际执行计划”——它不显示锁升级过程,只反映优化器预期
替代方案:用独立 UPDATE + INSERT 替代 MERGE 的真实代价
拆成两步确实能规避 MERGE 的扫描锁,但引入新问题:两次访问目标表、丢失原子性、需手动处理竞态(比如两次查询之间数据被删又插)。不过在高并发写场景下,这往往是更可控的选择。
典型模式是:
BEGIN TRY UPDATE t SET ... FROM target_table t INNER JOIN #staging s ON t.id = s.id; INSERT INTO target_table (...) SELECT ... FROM #staging s WHERE NOT EXISTS (SELECT 1 FROM target_table t WHERE t.id = s.id); END TRY
这里的关键不是“更快”,而是**锁行为可预测**:UPDATE 只锁匹配行,INSERT 只锁插入页,且两者可分别加索引优化。代价是应用层需容忍部分失败后重试,以及多一次索引查找。
- 务必给
#staging表建临时索引(尤其 ON 列),否则NOT EXISTS可能退化为嵌套循环+全表扫描 - 如果业务要求强一致性,可用
UPDLOCK, HOLDLOCK提示在 UPDATE 里提前锁定范围,但会降低并发度 - 千万避免在事务里先
SELECT再IF EXISTS ... UPDATE/INSERT—— 这种写法在并发下必然出现幻读和丢失更新
# ai
# 为什么
# sql
# Object
# if
# select
# 循环
# 堆
# using
# 并发
# bug
# 升级为
# 长时间
# 两次
# 它不
# 的是
# 主键
# 行数
# 是在
# 已被
# 会在
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
网站制作软件有哪些,制图软件有哪些?
百度输入法ai面板怎么关 百度输入法ai面板隐藏技巧
Java类加载基本过程详细介绍
Laravel Octane如何提升性能_使用Laravel Octane加速你的应用
Laravel怎么创建控制器Controller_Laravel路由绑定与控制器逻辑编写【指南】
Python数据仓库与ETL构建实战_Airflow调度流程详解
文字头像制作网站推荐软件,醒图能自动配文字吗?
JavaScript如何实现路由_前端路由原理是什么
韩国网站服务器搭建指南:VPS选购、域名解析与DNS配置推荐
购物网站制作费用多少,开办网上购物网站,需要办理哪些手续?
谷歌浏览器下载文件时中断怎么办 Google Chrome下载管理修复
Laravel怎么做数据加密_Laravel内置Crypt门面的加密与解密功能
Laravel如何生成API文档?(Swagger/OpenAPI教程)
网站设计制作书签怎么做,怎样将网页添加到书签/主页书签/桌面?
大连网站制作公司哪家好一点,大连买房网站哪个好?
昵图网官方站入口 昵图网素材图库官网入口
免费视频制作网站,更新又快又好的免费电影网站?
浏览器如何快速切换搜索引擎_在地址栏使用不同搜索引擎【搜索】
怎么用AI帮你设计一套个性化的手机App图标?
Win11摄像头无法使用怎么办_Win11相机隐私权限开启教程【详解】
Laravel怎么进行浏览器测试_Laravel Dusk自动化浏览器测试入门
Laravel怎么生成URL_Laravel路由命名与URL生成函数详解
Laravel如何使用Eloquent进行子查询
Linux系统命令中screen命令详解
Laravel如何使用模型观察者?(Observer代码示例)
JavaScript如何操作视频_媒体API怎么控制播放
Laravel如何实现一对一模型关联?(Eloquent示例)
*服务器网站为何频现安全漏洞?
如何快速搭建高效可靠的建站解决方案?
Laravel怎么处理异常_Laravel自定义异常处理与错误页面教程
海南网站制作公司有哪些,海口网是哪家的?
打造顶配客厅影院,这份100寸电视推荐名单请查收
uc浏览器二维码扫描入口_uc浏览器扫码功能使用地址
高端网站建设与定制开发一站式解决方案 中企动力
微信小程序 wx.uploadFile无法上传解决办法
Android利用动画实现背景逐渐变暗
Internet Explorer官网直接进入 IE浏览器在线体验版网址
如何续费美橙建站之星域名及服务?
Laravel如何实现全文搜索_Laravel Scout集成Algolia或Meilisearch教程
如何在企业微信快速生成手机电脑官网?
三星网站视频制作教程下载,三星w23网页如何全屏?
BootStrap整体框架之基础布局组件
如何在IIS服务器上快速部署高效网站?
如何确保西部建站助手FTP传输的安全性?
北京专业网站制作设计师招聘,北京白云观官方网站?
Laravel如何发送系统通知?(Notification渠道示例)
如何快速上传建站程序避免常见错误?
Laravel路由Route怎么设置_Laravel基础路由定义与参数传递规则【详解】
怎样使用JSON进行数据交换_它有什么限制
如何用狗爹虚拟主机快速搭建网站?


