打印面单时为什么为牺牲事务?

查看哪两个语句被互锁

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)与另一个进程被死锁在

原创  郑建华   2020-03-17   654人阅读  0 条评论

具体报错信息如下:

image.png

根据查找资料的结果显示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的值。死锁就这样发生了。

幸亏这个场景发生的前提是频繁查询以及频繁更新表。


根据代码报错处的情况来看,更加符合第二种场景

image.png

先执行查询,再执行更新操作,并且此处使用也较为频繁

如何解决呢?

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关键字。

image.png

将该方法设置为同步方法,用于解决同步问题,当有多条线程同时访问共享数据时,如果不进行同步,就会发生错误,java提供的解决方案是:只要将操作共享数据的语句在某一时段让一个线程执行完,在执行过程中,其他线程不能进来执行可以。

按道理来说,第二种方法应该也是能够解决问题的,所以先按照这种方式进行修复,而使用第一种方式进行处理,总感觉怪怪的,可能更适用于存储过程中的写法吧(个人观点)。

参考资料:

java中的synchronized同步代码块和同步方法的区别

【SqlServer】SqlServer中的更新锁(UPDLOCK)

sqlserver.jdbc.SQLServerException: 事务(进程 ID 246)与另一个进程被死锁在 锁 资源上,并且已被选作死锁牺牲品。请重新运行该事务

一次查找sqlserver死锁的经历

南来地,北往的,上班的,下岗的,走过路过不要错过!

======================个性签名=====================

之前认为Apple 的iOS 设计的要比 Android 稳定,我错了吗?

下载的许多客户端程序/游戏程序,经常会Crash,是程序写的不好(内存泄漏?刚启动也会吗?)还是iOS本身的不稳定!!!

如果在Android手机中可以简单联接到ddms,就可以查看系统log,很容易看到程序为什么出错,在iPhone中如何得知呢?试试Organizer吧,分析一下Device logs,也许有用.

我的开发工具

对于博客园里的网友,不敢称为叫"程序员"的人,你们攻击性太强,看来你们是不会想到我的用意的.园子里有不少人都非常喜欢Jeffrey,是因为它的第一版 框架设计 CLR via C#.
可是从第一版到现在的第三版,没有看到真正底层的东西,内容仅仅是比MSDN文档更丰富一些,可能是我的要求太高了吧.
也就是因为它很多时候会接触到微软开发人员,会经常聊聊某些问题而已,而它又将这些问题反应到书中.也许它就像一个小记者.
它的年龄大我们不多,我的孩子与它小儿子一般大,如果我能向它那样出入微软与它们开发人员长时间交流,不仅仅会牛成它这样.....
可是微软的开发人员不会扔太多时间在它这儿的.所以它会整天追着这个,赶它那个..屁颠个不停吧...
而它的另一版被称为好书的 Windows核心编程,更是没有什么深度可言,仅仅是将windows提供的api,以及内核功能再重申了一遍.
这些书对晋及编程知识是有些贡献的,再说一遍我不是在匾低谁,说说想法而已.

原文地址:https://www.cnblogs.com/ioriwellings/p/8657012.html