sql server中死锁会主动报错(如错误1205)并终止会话,而非单纯卡住;可通过错误日志搜索deadlock/1205、启用tf 1222、分析死锁图xml定位冲突语句,结合并发压力测试验证修复效果。

怎么看存储过程运行时有没有死锁
SQL Server 里死锁不是“卡住就一定是”,而是会主动报错并终止其中一个会话。最直接的信号就是应用抛出 Deadlock encountered ... ROLLBACK requested 这类错误,或者 SQL Server 错误日志里出现 1205 错误号。
但很多情况下,程序只记录了超时或空响应,没捕获具体错误——这时得靠日志反查:
- 打开 SQL Server 错误日志(
sp_readerrorlog),搜deadlock或1205 - 启用跟踪标志
1222(全局)或1204(旧版),让死锁信息以 XML 形式写入错误日志 - 如果用的是 Azure SQL,需开启
query_store并配合sys.query_store_runtime_stats查长时间阻塞
从死锁图 XML 里快速定位冲突语句
死锁图(Deadlock Graph)是 XML 格式,看着吓人,其实关键信息就三块:谁在等、谁被等、各自执行了哪条语句。别试图人肉解析整个 XML,盯住 executionStack 和 inputbuf 就够了。
常见陷阱是只看 inputbuf 里的语句,却忽略了它可能只是存储过程名(比如 EXEC dbo.proc_update_order),真正冲突的 UPDATE/SELECT 其实在 executionStack 的 frame 里。
- 用 SSMS 打开 .xdl 文件,右键“查看死锁图”,鼠标悬停在进程节点上,看 tooltip 里的
sqlhandle和实际语句 - 如果只有 XML 文本,搜
<frame procname="</code">,重点关注 <code>procname="adhoc"或procname="your_proc_name"下紧跟着的sqltext - 注意
isolationlevel字段:如果是repeatable read或serializable,比默认read committed更容易成环
存储过程中哪些写法最容易引发死锁
死锁本质是资源申请顺序不一致。存储过程里看似合理的逻辑,在并发下可能变成“你锁 A 再等 B,我锁 B 再等 A”。高频雷区集中在事务边界和索引设计上。
- 显式事务里混用不同顺序的表更新:比如
UPDATE orders→UPDATE customers在一个过程里,而另一个过程是反着来的 - WHERE 条件没走索引,导致扫描全表或大量页锁(
UPDATE ... WHERE status = 'pending'没给status建索引) - 在事务中调用外部依赖(如链接服务器、CLR 函数、HTTP 请求),延长持有锁时间
- 用
SELECT ... WITH (UPDLOCK, HOLDLOCK)人为扩大锁范围,但没配好索引,锁住不该锁的行
怎么验证改完后还死不死
本地单步调试没用,死锁是概率事件,必须模拟真实并发压力。重点不是“跑通”,而是“压出竞争路径”。
- 用
ostress.exe(RML Utilities)或 PowerShell 脚本并发调用存储过程,至少 10–20 线程,持续 30 秒以上 - 运行前先清空锁等待视图:
DBCC SQLPERF('sys.dm_os_wait_stats', CLEAR),之后查sys.dm_os_waiting_tasks中wait_type = 'LCK_M_XX'的堆积 - 别只盯着是否报错,观察
sys.dm_tran_locks里相同资源(resource_database_id+resource_associated_entity_id)是否频繁被多个request_session_id争抢
死锁不是修一次就永绝后患。只要业务逻辑里存在多表交叉更新、或数据分布发生倾斜(比如某订单 ID 被疯狂修改),就可能在某个流量高峰重现。日志采集和锁监控得一直开着。










