【问题标题】:Why does delete statement to a table, lock a select statement to the same table -- SQL Server为什么delete语句到一个表,select语句锁定到同一个表--SQL Server
【发布时间】:2013-12-25 10:38:32
【问题描述】:

我有一个用 Spring 3.0 编写的应用程序,并使用 SQL Server 2012 Enterprise 作为 RDS。

我一直在我的 DAO 操作中使用@Transactional,在这种特殊情况下

    @Transactional
    public void removeAll(String token) {

        cacheDao.delete(token);

    }

    public ClassSome getValue(String id) {
        return cacheDao.getValue(id);
    }

我的删除操作是事务性的,但未指定选择操作。这就是下面出现死锁的原因吗?

最近我在数据库中遇到了死锁。主要是上面这两个操作会死锁。

我不明白一个表上的查询如何相互死锁。这是 DBA 给我的示例死锁列表:

<deadlock-list>
<deadlock victim="process1180f5d498">
  <process-list>
   <process id="process1180f5d498" taskpriority="0" logused="0" waitresource="OBJECT: 6:1266103551:0 " waittime="2141" ownerId="1748561" transactionname="SELECT" lasttranstarted="2013-12-25T11:24:17.140" XDES="0x117ce7ba40" lockMode="S" schedulerid="30" kpid="4424" status="suspended" spid="87" sbid="0" ecid="0" priority="0" trancount="0" lastbatchstarted="2013-12-25T11:24:17.140" lastbatchcompleted="2013-12-25T11:24:17.137" lastattention="1900-01-01T00:00:00.137" clientapp="Microsoft SQL Server JDBC Driver" hostname="xxxx" hostpid="0" loginname="xxxx" isolationlevel="read committed (2)" xactid="1748561" currentdb="6" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
    <executionStack>
     <frame procname="adhoc" line="1" stmtstart="74" sqlhandle="0x02000000e0a92205aebcb9dd3f38539312f56b0c41af55990000000000000000000000000000000000000000">
select token, type, value from cache where token=@P0 and type=@P1     </frame>
     <frame procname="unknown" line="1" sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">
unknown     </frame>
    </executionStack>
    <inputbuf>
(@P0 varchar(8000),@P1 varchar(8000))select token, type, value from cache where token=@P0 and type=@P1                    </inputbuf>
   </process>
   <process id="process117d375c38" taskpriority="0" logused="0" waitresource="OBJECT: 6:1266103551:29 " waittime="2141" ownerId="1748560" transactionname="implicit_transaction" lasttranstarted="2013-12-25T11:24:17.140" XDES="0xf68c743a8" lockMode="X" schedulerid="64" kpid="9628" status="suspended" spid="96" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2013-12-25T11:24:17.140" lastbatchcompleted="2013-12-25T11:24:17.140" lastattention="1900-01-01T00:00:00.140" clientapp="Microsoft SQL Server JDBC Driver" hostname="xxxx" hostpid="0" loginname="xxx" isolationlevel="read committed (2)" xactid="1748560" currentdb="6" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128058">
    <executionStack>
     <frame procname="adhoc" line="1" stmtstart="74" sqlhandle="0x0200000023b477359eec278e4060e11f3a1c194cbed41cc10000000000000000000000000000000000000000">
delete cache where token=@P0 and type=@P1     </frame>
     <frame procname="unknown" line="1" sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">
unknown     </frame>
    </executionStack>
    <inputbuf>
(@P0 varchar(8000),@P1 varchar(8000))delete cache where token=@P0 and type=@P1                    </inputbuf>
   </process>
  </process-list>
  <resource-list>
   <objectlock lockPartition="0" objid="1266103551" subresource="FULL" dbid="6" objectname="xxxx.dbo.cache" id="lock10034ef580" mode="X" associatedObjectId="1266103551">
    <owner-list>
     <owner id="process117d375c38" mode="X"/>
    </owner-list>
    <waiter-list>
     <waiter id="process1180f5d498" mode="S" requestType="wait"/>
    </waiter-list>
   </objectlock>
   <objectlock lockPartition="29" objid="1266103551" subresource="FULL" dbid="6" objectname="xxxx.dbo.cache" id="lock1011729e00" mode="IS" associatedObjectId="1266103551">
    <owner-list>
     <owner id="process1180f5d498" mode="IS"/>
    </owner-list>
    <waiter-list>
     <waiter id="process117d375c38" mode="X" requestType="wait"/>
    </waiter-list>
   </objectlock>
  </resource-list>
</deadlock>
</deadlock-list>

编辑 #1

-- Results for: 
    SELECT  i.name, i.allow_row_locks, i.allow_page_locks
    FROM    sys.indexes i
    WHERE   i.object_id = OBJECT_ID(N'dbo.cache')

name    allow_row_locks allow_page_locks
NULL    1   1
IND_cache_token_type    1   1

【问题讨论】:

  • 查看快照隔离。这是解决 SELECT 锁定问题的常见对策。
  • @usr:请停止提供不完整的建议。为什么是 SI 而不是 RCSI?
  • @BogdanSahlean 不确定您指的是什么。对于这个问题,我没有时间给出完整的答案。这就是为什么我建议研究这个话题。我不建议只打开它。如果你愿意,我会和你聊聊这个。
  • @usr:请看我的回答,注意#3。
  • @BogdanSahlean 我确实看到了这一点,我投了赞成票。不过,对于只读事务,SI 是 几乎是一个简单的决定。它只是让读者远离锁定和阻塞。我不会因为它可能不合适而忽略它。它可能是合适的,但必须被理解和测试。

标签: sql sql-server sql-server-2012 deadlock


【解决方案1】:

假设我们有这些表:

Department (department-id (pk) , name);
Emp (EMP_ID (pk), department-id (FK), name);

当从部门删除一行时,为了保持完整性,RDMBS 必须在Emp 表中搜索并找到所有子记录。

当Emp表中的department-id列没有索引时,将对Emp进行全表扫描,RDBMS会在操作前锁定整个Emp表。

如果Emp 有很多记录,则操作将需要很长时间,同时如果其他事务试图操纵Emp,则可能会发生超时或死锁。

强烈建议在外键上创建索引以防止出现问题。

需要更多信息来调查您的问题,我刚刚提到了一个常见问题。

【讨论】:

  • 您好,糟糕的架构设计可能会导致死板,例如嵌套或两个面部提交的异常处理很差。另一个原因是如果您使用的是 ORM 框架,当 ORM 发现域数据变脏时,可能会发生一些未被注意到的更新。跟踪场景并查看进程上运行的 SQL。
【解决方案2】:

1) 很可能,此 DL 的原因是缺少索引。尝试创建以下索引:

CREATE /*UNIQUE*/ INDEX IX_Cache_Token_Type
ON dbo.Cache (Token, Type)
INCLUDE (Value)

这个索引应该对两种类型的查询都有帮助:

select token, type, value from cache where token=@P0 and type=@P1 

和

delete cache where token=@P0 and type=@P1

2) 如果这个索引没有消除这些 DL,那么应该问自己为什么两个并发事务尝试删除/读取同一行?

3) 切换到快照隔离并不是一个简单的决定:

编辑#1:

DECLARE @sqlhandle1 VARBINARY(64);
SET @sqlhandle1 = 0x02000000e0a92205aebcb9dd3f38539312f56b0c41af55990000000000000000000000000000000000000000
SELECT  qp.query_plan
FROM    sys.dm_exec_query_plan(@sqlhandle1) qp

DECLARE @sqlhandle2 VARBINARY(64);
SET @sqlhandle2 = 0x0200000023b477359eec278e4060e11f3a1c194cbed41cc10000000000000000000000000000000000000000
SELECT  qp.query_plan
FROM    sys.dm_exec_query_plan(@sqlhandle2) qp

编辑#2: 我们还需要检查是否允许行/页锁:

SELECT  i.name, i.allow_row_locks, i.allow_page_locks
FROM    sys.indexes i
WHERE   i.object_id = OBJECT_ID(N'dbo.cache')

【讨论】:

  • 因为在某种程度上,对这个缓存表的更新是使用 delete first insert second 来实现的。
  • @LostMohican:这仍然无法解释为什么 Select 会尝试读取已删除的行/更新的行。您需要添加更多信息。你在缓存表上有插入/删除触发器吗?
  • @LostMohican:你能告诉我以下查询返回什么结果:SELECT t.lock_escalation_desc FROM sys.tables t WHERE t.object_id = OBJECT_ID(N'dbo.cache')?自动还是表格?
  • 它返回了表。这是什么意思?
  • “编辑#1 处的查询返回空。” + 我的查询 Edit #2 的输出:我确定您已在同一服务器和数据库上执行了此查询? 也许您在生产服务器上遇到了死锁,但您在开发/测试数据库服务器上执行了这些查询!例如,SELECT ... FROM sys.indexes i ... 的输出显示dbo.cache 表是一个 HEAP 表,但 XML 死锁图表明同一个表已分区。这意味着您在 dbo.cache 表上有一个聚集索引。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-21
  • 2021-01-05
  • 1970-01-01
  • 2017-02-17
  • 2022-11-11
  • 1970-01-01
相关资源
最近更新 更多