查看哪两个语句被互锁
SELECT wt.blocking_session_id AS BlockingSessesionId ,sp.program_name AS ProgramName ,COALESCE(sp.LOGINAME, sp.nt_username) AS HostName ,ec1.client_net_address AS ClientIpAddress ,db.name AS DatabaseName ,wt.wait_type AS WaitType ,ec1.connect_time AS BlockingStartTime ,wt.WAIT_DURATION_MS/1000 AS WaitDuration ,ec1.session_id AS BlockedSessionId
,ec2.session_id as blockingSessionID
,h1.TEXT AS BlockedSQLText
,h2.TEXT AS BlockingSQLText
FROM sys.dm_tran_locks AS tl INNER JOIN sys.databases db ON db.database_id = tl.resource_database_id INNER JOIN sys.dm_os_waiting_tasks AS wt ON tl.lock_owner_address = wt.resource_address INNER JOIN sys.dm_exec_connections ec1 ON ec1.session_id = tl.request_session_id INNER JOIN sys.dm_exec_connections ec2 ON ec2.session_id = wt.blocking_session_id LEFT OUTER JOIN master.dbo.sysprocesses sp ON SP.spid = wt.blocking_session_id CROSS APPLY sys.dm_exec_sql_text(ec1.most_recent_sql_handle) AS h1 CROSS APPLY sys.dm_exec_sql_text(ec2.most_recent_sql_handle) AS h2
158 Microsoft SQL Server Management Studio - 查询 enspm 172.17.60.117 HW_MangerData_20170817 LCK_M_S 2018-03-27 08:59:17.937 18 161 BEGIN TRANSACTION select * from tHWDeliveryList b inner join tHWOrderInfo a on a.cOrderCode = b.cOrderCode inner join tHWDeliverySub c on c.cDeliveryOrder = b.cDeliveryOrder inner join tHWDelivery d on d.cBatchCode = c.cBatchCode inner join tHWClientInfo e on e.cClientCode = a.cClientCode inner join tHWGoodInfo f on a.cGoodCode = f.cGoodCode where a.cOrderID = '20180109001046' --ROLLBACK TRANSACTION (@1 varchar(8000))UPDATE [tHWDeliveryList] set [iFinish] = [ifinish] WHERE [cOrderCode]=@1 161 Microsoft SQL Server Management Studio - 查询 enspm 172.17.60.117 HW_MangerData_20170817 LCK_M_X 2018-03-27 08:59:00.967 0 158 (@1 varchar(8000))UPDATE [tHWDeliveryList] set [iFinish] = [ifinish] WHERE [cOrderCode]=@1 BEGIN TRANSACTION select * from tHWDeliveryList b inner join tHWOrderInfo a on a.cOrderCode = b.cOrderCode inner join tHWDeliverySub c on c.cDeliveryOrder = b.cDeliveryOrder inner join tHWDelivery d on d.cBatchCode = c.cBatchCode inner join tHWClientInfo e on e.cClientCode = a.cClientCode inner join tHWGoodInfo f on a.cGoodCode = f.cGoodCode where a.cOrderID = '20180109001046' --ROLLBACK TRANSACTION
--1. 比如先进行了打印面单
BEGIN TRANSACTION --3.补打查询后,打印线程执行这里就会发生互锁.查询的事务会被自动终止, update tHWDeliveryList set iFinish = ifinish where cOrderCode = '20180109000059' 这里冲突 update tHWOrderInfo set iType = 1 where corderid = '20180109000365' -- cOrderID没有索引,全局扫表,获取了表锁,直到事务结束才会释放,其它读的读操作也要等待 --select * from tHWOrderInfo a where corderid = '20180109000365' --ROLLBACK TRANSACTION SELECT l.request_session_id, DB_NAME(l.resource_database_id),OBJECT_NAME(p.object_id), l.resource_description,l.request_type, l.request_status,request_mode FROM sys.dm_tran_locks AS l LEFT JOIN sys.partitions AS p ON l.resource_associated_entity_id=p.hobt_id
--2. 这时有人进行面单补打的查询.
BEGIN TRANSACTION select * from tHWDeliveryList b -- 这里冲突 inner join tHWOrderInfo a on a.cOrderCode = b.cOrderCode --这里要等待表锁头释放 inner join tHWDeliverySub c on c.cDeliveryOrder = b.cDeliveryOrder inner join tHWDelivery d on d.cBatchCode = c.cBatchCode inner join tHWClientInfo e on e.cClientCode = a.cClientCode inner join tHWGoodInfo f on a.cGoodCode = f.cGoodCode where a.cOrderID = '20180109001046' --ROLLBACK TRANSACTION
错误信息: 事务(进程 ID 156)与另一个进程被死锁在 锁 资源上,并且已被选作死锁牺牲品。请重新运行该事务。 在 dysoft.Data.CAccessDataClass.GetDataSet(CommandType commandType, String strSQL, SqlParameter[] commandParameters, DataSet dts, String strTableName) 在 HiViAccess.HWShippingDAO.getShippingOrderInfo(String innerOrderID) 在 HiViModel.HWShippingModel.getShippingOrderInfo(String innerOrderID) 在 dysoft.HiVi.webPage.HiviUIPage.ShippingManagement.SMShippingList.btnPrint_Click(Object sender, EventArgs e) 在 System.Web.UI.WebControls.Button.OnClick(EventArgs e) 在 System.Web.UI.WebControls.Button.RaisePostBackEvent(String eventArgument) 在 System.Web.UI.WebControls.Button.System.Web.UI.IPostBackEventHandler.RaisePostBackEvent(String eventArgument) 在 System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument) 在 System.Web.UI.Page.RaisePostBackEvent(NameValueCollection postData) 在 System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) 错误对象: .Net SqlClient Data Provider 信息提示: 无 访问链接: http://huiweiadmin.misvip.net/HiviUIPage/ShippingManagement/SMShippingList.aspx
USE [HW_MangerData_20170817] GO /****** Object: StoredProcedure [dbo].[pHWDeliverGoodsHandle] Script Date: 02/22/2021 15:09:12 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER OFF GO /************************************************************************************************************* 过程名称: pHWDeliverGoodsHandle 目 的: 发货处理 调 用 者: 创建日期: 2017-06-24 作 者: 龚春平 修改日期: 修改内容: 输入参数: 输出参数: 重要提示: 涉 及 表: 讨 论: 使用样例:exec pHWDeliverGoodsHandle '1111111222222','','','00000001','' *************************************************************************************************************/ ALTER proc [dbo].[pHWDeliverGoodsHandle] ( @cBarCode varchar(30), --条码 @cGoodID varchar(30), --货号 @cSize varchar(30), --尺码 @cWarehouseCode varchar(512), --发货仓编码 @cScanCodeHuman varchar(30) --扫货人 ) as create table #tOrderInfo ( iID numeric(20) identity , cOrderID varchar(30) null, dCreate datetime null, iType int null, --1、普通客户 2、天猫客户 ) CREATE TABLE #tTemp_Warehouse --将发货仓信息串先“|”拆分 ( cWarehouseCode varchar(30) null ) declare @sql varchar(1000),@iCount int set @sql='insert into #tTemp_Warehouse (cWarehouseCode) select col='''+ replace(@cWarehouseCode,'|',''' union all select ''')+'''' exec (@sql) ---------------------------通过条码或者货号和尺码,查找该货品的全部订单(未扫货的)----------------------- if( rtrim(ISNULL(@cBarCode,''))<> '') begin insert into #tOrderInfo(cOrderID,dCreate,iType) select b.cOrderID,b.dCreate,g.iType from tHWDeliveryList a inner join tHWOrderInfo b on a.cOrderCode = b.cOrderCode LEFT join tHWDeliverySub c on c.cDeliveryOrder = a.cDeliveryOrder LEFT join tHWDelivery d on d.cBatchCode = c.cBatchCode LEFT join tHWGoodInfo f on a.cGoodCode = f.cGoodCode left join tHWClientInfo g on b.cClientCode = g.cClientCode where f.cBarCode = @cBarCode and d.iType = 1 and d.cWarehouseCode in (select cWarehouseCode from #tTemp_Warehouse ) and d.iState <> 3 and a.iState = 1 and a.iPrint = 1 and a.iScanCode = 1 order by b.dCreate end else begin insert into #tOrderInfo(cOrderID,dCreate,iType) select b.cOrderID,b.dCreate,g.iType from tHWDeliveryList a inner join tHWOrderInfo b on a.cOrderCode = b.cOrderCode LEFT join tHWDeliverySub c on c.cDeliveryOrder = a.cDeliveryOrder LEFT join tHWDelivery d on d.cBatchCode = c.cBatchCode LEFT join tHWGoodInfo f on a.cGoodCode = f.cGoodCode left join tHWClientInfo g on b.cClientCode = g.cClientCode where f.cGoodID = @cGoodID and f.cSize = @cSize and d.iType = 1 and d.cWarehouseCode in (select cWarehouseCode from #tTemp_Warehouse ) and d.iState <> 3 and a.iState = 1 and a.iPrint = 1 and a.iScanCode = 1 order by b.dCreate end ---------------------------通过条码或者货号和尺码,查找该货品的全部订单(未扫货的)----------------------- --规则顺序:天猫订单单件--天猫订单多件--客户订单单件--客户订单多件”这个顺序 ---------------------------根据订单数量和下单日期获取一条订单----------------------- --select top 1 a.cOrderID,count(a.cOrderID)as iCount, max(a.dCreate) as dCreate into #Temp --from tHWOrderInfo a,#tOrderInfo b,tHWDeliveryList c where a.cOrderID = b.cOrderID --and a.cOrderCode = c.cOrderCode and c.iState=1 --group by a.cOrderID --order by iCount , dCreate create table #Temp ( cOrderID varchar(30) null, dCreate datetime null, iCount int null, ) --1、普通客户 2、天猫客户 --优先天猫客户 insert into #Temp(cOrderID,iCount,dCreate) select top 1 cOrderID,count(cOrderID)as iCount, max(dCreate) as dCreate from #tOrderInfo where iType = 2 group by cOrderID order by iCount , dCreate select @iCount = COUNT(1) from #Temp if(ISNULL(@iCount,0) = 0) begin --普通客户 insert into #Temp(cOrderID,iCount,dCreate) select top 1 cOrderID,count(cOrderID)as iCount, max(dCreate) as dCreate from #tOrderInfo where iType = 1 group by cOrderID order by iCount , dCreate end ---------------------------根据订单数量和下单日期获取一条订单----------------------- select a.cOrderID,a.cOrderCode, d.cBatchCode, a.iType, a.cClientCode, a.cDate, a.dDate, a.dClientOrder, a.cGoodCode, a.cGoodID, a.cSize, a.cBrand, a.nPrice, a.nSalesPrice, a.cLinkman, a.cPhone, a.cAddress, a.cClientOrderID, a.cWWName, a.cSendType, a.dScanCode, a.cScanCodeHuman, a.cCourierCode,a.iOrderSort, (case a.iOrderSort when 1 then '仓库' when 2 then '实体店' end) as cOrderSort, a.iState,a.iNumber,a.cOperator, a.dCreate, (select cText from tHWTypeValue where cType='OrderStatus' and cValue = a.iState) cStatus, (case a.iState when 5 then '已取消' else '未取消' end) as cCancel, b.cDeliveryOrder, b.iScanCode, b.iPrint, b.iFinish, (select c.cWarehouseName from tHWWarehouse c where c.cWarehouseCode = a.cWarehouseCode) as cWarehouseName, (case b.iScanCode when 1 then '未扫' else '已扫' end) as cScanCode, (case b.iPrint when 1 then '未打印' else '已打印' end) as cPrint, (case b.iFinish when 1 then '未完成' else '已完成' end) as cFinish, (select c.cWarehouseName from tHWWarehouse_S c where c.cWarehouseCode = d.cWarehouseCode) as cWareHouseNameT, e.cClientName, f.cBarCode into #temp_Return from tHWDeliveryList b inner join tHWOrderInfo a on a.cOrderCode = b.cOrderCode inner join tHWDeliverySub c on c.cDeliveryOrder = b.cDeliveryOrder inner join tHWDelivery d on d.cBatchCode = c.cBatchCode inner join tHWClientInfo e on e.cClientCode = a.cClientCode inner join tHWGoodInfo f on a.cGoodCode = f.cGoodCode where 1=1 and e.iState =2 and a.iState <> 5 and a.cOrderID = (select top 1 cOrderID from #Temp) order by a.cOrderCode declare @cOrderCode varchar(32),@cBatchCode varchar(32) ,@dCreate datetime set @dCreate = GETDATE() if( rtrim(ISNULL(@cBarCode,''))<> '') begin select top 1 @cOrderCode = cOrderCode,@cBatchCode=cBatchCode from #temp_Return where cBarCode = @cBarCode and cScanCode = '未扫' and iState = 3 order by cOrderCode end else begin select top 1 @cOrderCode = cOrderCode,@cBatchCode=cBatchCode from #temp_Return where cScanCode = '未扫' and cGoodID = @cGoodID and cSize = @cSize and iState = 3 order by cOrderCode end if @cOrderCode is null Goto exitSection BEGIN TRANSACTION update tHWOrderInfo set cScanCodeHuman = @cScanCodeHuman,dScanCode = @dCreate where cOrderCode =@cOrderCode and iState = 3 if @@error <> 0 Goto PROBLEM update tHWDeliveryList set iScanCode = 2 where cOrderCode = @cOrderCode if @@error <> 0 Goto PROBLEM update tHWDelivery set iState = 2 where iState = 1 and cBatchCode = @cBatchCode if @@error <> 0 Goto PROBLEM insert into tHWScanCode(cOrderCode,cScanCodeHuman,dScanCode) select @cOrderCode,@cScanCodeHuman,@dCreate if @@error <> 0 Goto PROBLEM COMMIT TRANSACTION update #temp_Return set iScanCode = 2 ,cScanCode='已扫',cScanCodeHuman = @cScanCodeHuman,dScanCode = @dCreate where cOrderCode =@cOrderCode select * from #temp_Return --当前页面扫货数量: 已发货数量: 未发货数量: 总数量: 本机用户扫货数量: 本机 扫货数量: select a.iCount_DQR,b.iCount_YF,c.iCount_WF,d.iCount_Sum from (select COUNT(1) as iCount_DQR from #temp_Return where cScanCode = '已扫' and cScanCodeHuman = @cScanCodeHuman) a, (select COUNT(1) as iCount_YF from #temp_Return where cScanCode = '已扫') b, (select COUNT(1) as iCount_WF from #temp_Return where cScanCode = '未扫') c, (select COUNT(1) as iCount_Sum from #temp_Return )d Return PROBLEM: ROLLBACK TRANSACTION exitSection:
错误信息: 事务(进程 ID 51)与另一个进程被死锁在 锁 资源上,并且已被选作死锁牺牲品。请重新运行该事务。
在 dysoft.Data.CAccessDataClass.GetScalar(CommandType commandType, String strSQL, SqlParameter[] commandParameters)
在 HiViAccess.HWShippingDAO.setPrintFinsish(String innerOrderID, String expressNo, String targetCode1, String targetCode2, String extend)
在 HiViModel.HWShippingModel.setPrintFinsish(String innerOrderID, String expressNo, String targetCode1, String targetCode2, String extend)
访问链接: http://huiweiadmin.misvip.net/HiviUIPage/ShippingManagement/SMReprint.aspx
USE [HW_MangerData_20170817] GO /****** Object: StoredProcedure [dbo].[pHWDeliverGoodsEnd] Script Date: 02/09/2021 15:49:17 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER OFF GO /************************************************************************************************************* 过程名称: pHWDeliverGoodsEnd 目 的: 某一订单发货完成调用(打印快递单) 调 用 者: 创建日期: 2017-06-24 作 者: 龚春平 修改日期: 修改内容: 输入参数: 输出参数: 重要提示: 涉 及 表: 讨 论: 使用样例:exec pHWDeliverGoodsEnd '20170920000604','617209539099','371', '', '20171025150450064' *************************************************************************************************************/ ALTER proc [dbo].[pHWDeliverGoodsEnd] ( @cOrderID varchar(30), --订单ID @cCourierCode varchar(30), --快递单号 @cTargetCode1 varchar(32), @cTargetCode2 varchar(32), @cExtend varchar(32) ) as select a.cOrderCode,b.iScanCode into #tHWOrderInfo from tHWOrderInfo a inner join tHWDeliveryList b on b.cOrderCode = a.cOrderCode where a.cOrderID = @cOrderID and b.iPrint = 1 declare @iCount int select @iCount = COUNT(1) from #tHWOrderInfo if @iCount = 0 goto exitSection BEGIN TRANSACTION -- and cScanCodeHuman is not null 扫描人的条件加不加,再定吧, 08-31号定了, 扫描人条件加上 代表已勾选,未勾选的标记为配货失败. update tHWOrderInfo set iState=6, cCourierCode = @cCourierCode,cTargetCode1=@cTargetCode1, cTargetCode2=@cTargetCode2, cExtend=@cExtend where cOrderID = @cOrderID and dScanCode is not null -- 标记配货失败 update tHWOrderInfo set iState = 4 from tHWOrderInfo a where a.cOrderID = @cOrderID and a.iState = 3 and a.cScanCodeHuman is null if @@error <> 0 Goto PROBLEM update tHWDeliveryList set iState = 4, iPrint=2, iFinish = 2 from tHWDeliveryList a, #tHWOrderInfo b where a.cOrderCode = b.cOrderCode and b.iScanCode = 2 update tHWDeliveryList set iState = 4, iPrint=1, iFinish = 2 from tHWDeliveryList a, #tHWOrderInfo b where a.cOrderCode = b.cOrderCode and b.iScanCode = 1 if @@error <> 0 Goto PROBLEM COMMIT TRANSACTION select @iCount Return PROBLEM: ROLLBACK TRANSACTION exitSection: select 0
当 iID字段没有索引时,会产生死锁的样例 ,因为update会锁定整个table_1表,全局扫描 begin transaction select * from Table_1 where iID = 1 --step 1 update Table_1 set name = name where iID = 2 --step3 select * from Table_1 where iID = 2 --step 5 --ROLLBACK TRANSACTION ----------------------------------------------------------------- begin transaction select * from Table_1 where iID = 2 --step 2 update Table_1 set name = name where iID = 1 --step 4 ---ROLLBACK TRANSACTION
BEGIN TRANSACTION update tHWOrderInfo set iType = 1 where corderid = '20191211000233' update tHWOrderInfo set iState=6, cCourierCode = cCourierCode, cTargetCode1=cTargetCode1, cTargetCode2=cTargetCode2, cExtend=cExtend where cOrderID = '20191211000233' and dScanCode is not null -- 标记配货失败 update tHWOrderInfo set iState = 4 from tHWOrderInfo a where a.cOrderID = '20191211000233' and a.iState = 3 and a.cScanCodeHuman is null -- step 1
update tHWDelivery set iState = 2
where
--iState = 1 and
cBatchCode = 'T2019121101' --step 3
update tHWDeliveryList set iState = 4, iPrint=2, iFinish = 2 from tHWDeliveryList a where a.cOrderCode = '20191205000236' --step 3 ,查询的事务被牺牲 --ROLLBACK TRANSACTION ----------------------------------------------
错误信息: 事务(进程 ID 158)与另一个进程被死锁在 锁 资源上,并且已被选作死锁牺牲品。请重新运行该事务。
在 dysoft.Data.CAccessDataClass.GetDataSet(CommandType commandType, String strSQL, SqlParameter[] commandParameters, DataSet dts, String strTableName)
在 HiViAccess.HWShippingDAO.getShippedOrderInfo(String innerOrderID)
public HiViUtil.HWSendOrderInfoDataSet getShippedOrderInfo(string innerOrderID); 补打发货查询 BEGIN TRANSACTION declare @count int set @count = 1 while @count< 100 begin select a.cOrderCode, a.cOrderID, * from tHWDeliveryList b -- 这里冲突 inner join tHWOrderInfo a on a.cOrderCode = b.cOrderCode --这里要等待表锁头释放 inner join tHWDeliverySub c on c.cDeliveryOrder = b.cDeliveryOrder inner join tHWDelivery d on d.cBatchCode = c.cBatchCode inner join tHWClientInfo e on e.cClientCode = a.cClientCode inner join tHWGoodInfo f on a.cGoodCode = f.cGoodCode select a.cOrderCode, a.cOrderID, * from tHWDeliveryList b -- 这里冲突 inner join tHWOrderInfo a on a.cOrderCode = b.cOrderCode --这里要等待表锁头释放 inner join tHWDeliverySub c on c.cDeliveryOrder = b.cDeliveryOrder inner join tHWDelivery d on d.cBatchCode = c.cBatchCode inner join tHWClientInfo e on e.cClientCode = a.cClientCode inner join tHWGoodInfo f on a.cGoodCode = f.cGoodCode select a.cOrderCode, a.cOrderID, * from tHWDeliveryList b -- 这里冲突 inner join tHWOrderInfo a on a.cOrderCode = b.cOrderCode --这里要等待表锁头释放 inner join tHWDeliverySub c on c.cDeliveryOrder = b.cDeliveryOrder inner join tHWDelivery d on d.cBatchCode = c.cBatchCode inner join tHWClientInfo e on e.cClientCode = a.cClientCode inner join tHWGoodInfo f on a.cGoodCode = f.cGoodCode select a.cOrderCode, a.cOrderID, * from tHWDeliveryList b -- 这里冲突 inner join tHWOrderInfo a on a.cOrderCode = b.cOrderCode --这里要等待表锁头释放 inner join tHWDeliverySub c on c.cDeliveryOrder = b.cDeliveryOrder inner join tHWDelivery d on d.cBatchCode = c.cBatchCode inner join tHWClientInfo e on e.cClientCode = a.cClientCode inner join tHWGoodInfo f on a.cGoodCode = f.cGoodCode set @count = @count +1 end --step 2 where a.cOrderID = '20191211000233' --ROLLBACK TRANSACTION
select * from tHWDeliveryList where cOrderCode = '20191205000236'
select * from tHWDeliverySub where cDeliveryOrder = 'T201912110111'
select * from tHWDelivery where cBatchCode = 'T2019121101'
SQLServerException:事务(进程 ID 79)与另一个进程被死锁在
具体报错信息如下:
根据查找资料的结果显示sqlserver的死锁,包括两种场景
场景1:
事务A: delele from table1 where id= 1; select * from table2; 事务B: delele from table2 where id= 1; select * from table1; |
要理解这个场景,首先要了解两种锁的区别:X锁和S锁。简单的说,就是当试图删除、更新表时,我们会在该表上加一个X锁,这个X锁是排他锁,影响是除非表上的X锁被释放,不然其它人无法再加如何锁;而S锁是一个共享锁,它允许同一时间有多个共享锁存在,但是除了X锁以外。
假设场景1是并行执行的,事务1开始对table1加上了X锁,同时事务2也对table2加上了X锁。接下来它们又分别试图请求,在对方已经加上了X锁的表上加上S锁。从前面得知,X锁是排他锁,而事务的特性是,所有的锁资源都会在事务完成以后才释放。 这时候事务间各自拥有对方请求的资源,又同时请求对方拥有的资源,并且要释放自身的资源,先要请求到对方的资源,就发生死锁了。
场景2:
事务A:delete from table1 where id =(1,..,n); 事务B:select c1,c2,c3 from table1 where id = (1,..,n); table1的索引如下,物理索引id,逻辑索引c1,c2。 |
据说这个场景的死锁比较普遍,但是很难理解,因为它们由始至终只操作一个表。要理解这个,还需从sqlserver中的bookmark search说起:如果select 语句中,查询的栏位不包含在逻辑索引中,比如c3,那么sqlserver将试图在使用物理索引id来查找c3的值。
现在再来看一下过程中发生的锁,select语句会在逻辑索引(c1,c2)上加上S锁,然后为了返回不在逻辑索引的栏位(c3),它还需要在物理索引(id)上加上S锁;而更新呢?我们都知道,更新表时,我们需要对物理索引(id)加上排他锁以完成表的更新,并且随后还会被要求更新逻辑索引(c1,c2)。所以事务A先请求对id列的X锁,随后请求c1,c2列的X锁;而事务B会先请求c1,c2的S锁,接着请求id的S锁来返回c3的值。死锁就这样发生了。
幸亏这个场景发生的前提是频繁查询以及频繁更新表。
根据代码报错处的情况来看,更加符合第二种场景
先执行查询,再执行更新操作,并且此处使用也较为频繁
如何解决呢?
1、根据查找的资料,可以通过SqlServer中的更新锁(UPDLOCK)
UPDLOCK.UPDLOCK 的优点是允许您读取数据(不阻塞其它事务)并在以后更新数据,同时确保自从上次读取数据后数据没有被更改。当我们用UPDLOCK来读取记录时可以对取到的记录加上更新锁,从而加上锁的记录在其它的线程中是不能更改的只能等本线程的事务结束后才能更改.
比如:
用户A读一条记录,然后修改该条记录
这时用户B修改该条记录
这里用户A的事务里锁的性质由共享锁(S)企图上升到独占锁(X)(for update),而用户B里的独占锁(X)由于A有共享锁(S)存在所以必须等A释放掉共享锁(S),而A由于B的独占锁(X)而无法上升的独占锁(X)也就不可能释放共享锁(S),于是出现了死锁。
这种死锁比较隐蔽,但其实在稍大点的项目中经常发生。
解决方法:
让用户A的事务(即先读后写类型的操作),在select 时就是用Update lock
语法如下:
select * from table1 with(updlock) where ....
2、系统中还存在一个同类型的业务,医嘱执行,代码的模式也和这种类似,都是属于先查询,再更新的操作。但是运行过程中,此处业务就没有报这个错误。因为该方法上面,添加了synchronized关键字。
将该方法设置为同步方法,用于解决同步问题,当有多条线程同时访问共享数据时,如果不进行同步,就会发生错误,java提供的解决方案是:只要将操作共享数据的语句在某一时段让一个线程执行完,在执行过程中,其他线程不能进来执行可以。
按道理来说,第二种方法应该也是能够解决问题的,所以先按照这种方式进行修复,而使用第一种方式进行处理,总感觉怪怪的,可能更适用于存储过程中的写法吧(个人观点)。
参考资料:
java中的synchronized同步代码块和同步方法的区别
【SqlServer】SqlServer中的更新锁(UPDLOCK)
sqlserver.jdbc.SQLServerException: 事务(进程 ID 246)与另一个进程被死锁在 锁 资源上,并且已被选作死锁牺牲品。请重新运行该事务