SQL Server死锁的解决过程
某现场报一个SQL死锁,于是开启了1222跟踪:
1 | dbcc traceon(1222,-1) |
一段时间之后拷贝ERROR文件查找相关信息,比较有用的摘录出来如下:
语句一:
1 2 3 4 5 6 | select study_iuid,station_aet,modality,accession_no,patient_fk,item_attrs,start_datetime from worklist w WITH (readpast), mwl_item m where w.TAG_STUDY_INSTANCE_UID=m.study_iuid and isread= '1' and (TAG_SPS_STATUS is null or TAG_SPS_STATUS= 'SCHEDULED' or TAG_SPS_STATUS= 'Discontinued' ) and TAG_SPS_START_DATE between @P0 and @P1 and not exists ( select 1 from mpps b where b.study_iuid=m.study_iuid) |
语句二:
1 2 | INSERT INTO mwl_item (created_time, updated_time, sps_id, start_datetime, station_aet, station_name, modality, perf_physician, perf_phys_fn_sx, perf_phys_gn_sx, perf_phys_i_name, perf_phys_p_name, req_proc_id, accession_no, study_iuid, item_attrs, sps_status, patient_fk) VALUES (@P0, @P1, @P2, @P3, @P4, @P5, @P6, @P7, @P8, @P9, @P10, @P11, @P12, @P13, @P14, @P15, @P16, @P17); |
相关的死锁资源如下:
1 2 3 4 5 6 7 8 9 10 11 | resource-list pagelock fileid=1 pageid=6996 dbid=8 objectname=Worklist.dbo.mwl_item id=lock19825c100 mode=IX associatedObjectId=72057594039697408 owner-list owner id=process984d048 mode=IX waiter-list waiter id=process60e9708 mode=S requestType=wait pagelock fileid=1 pageid=11086 dbid=8 objectname=Worklist.dbo.mwl_item id=lock1b087b100 mode=S associatedObjectId=72057594039697408 owner-list owner id=process60e9708 mode=S waiter-list waiter id=process984d048 mode=IX requestType=wait |
可以明显的看到是select语句与insert语句产生了死锁,争用的资源分别6996和11086这两个page,使用dbcc page查看这两个页:
1 2 3 4 5 6 7 | dbcc traceon(3604) go dbcc page( 'Worklist' ,1,6996,3) dbcc traceon(3604) go dbcc page( 'Worklist' ,1,11086,3) |
1.select进程60e9708持有页11086的S锁,同时请求页6996的S锁。
2.insert进程984d048持有页6996的IX锁,同时请求页11086的IX锁。
下图为抓到的相关deadlock graph,与上文中的进程ID不同,不过阻塞语句和情况都一样。
其原理为:
1.由最初的303进程的insert语句产生数据页4419的IX锁,同时请求BLOB页17741上的IX锁以便插入BLOB数据,但此时数据页17741上已有与IX不兼容的S锁。
2.而select语句会在BLOB页17741上加S锁,返回请求数据页4419的行数据失败,因为4419页被insert语句加了不兼容的IX锁,因此两个进程形成死锁。
这个查询语句是查询当天的数据(这意味着查询基本集中在最后几页),而mwl_item表的主键是自增长的ID,因此插入也是会插入到最后一页。这样造成查询与插入频繁在最后一页生成死锁。
可能的几种优化方式:
1.优化select语句使其走mwl_item表的非聚集索引,但是由于本例是insert语句,insert会更新表上的所有索引,因此除非将BLOB列加入包含列,否则无法解决,但BLOB列是不能出现在索引中的。
2.通知开发优化相关代码的执行频度来避免死锁。
3.升级程序版本,新版本没有mwl_item表。
4.将with readpast换成with nolock,读不加共享锁,但会有脏读。
5.修改全局事务隔离级别为read committed snapshot。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)