Sql Server 2012 合并死锁

Deadlock In Sql Server 2012 With Merge

我们的应用程序中有一个复杂的发布模型,允许不同的用户看到不同的对象。我们生成哪些用户可以看到哪些对象,因为视图决定了这很慢。我们有一个每 10 分钟运行一次的后台服务,它使用合并语句来确保存储的 table 是最新的,我们还有一个在请求开始时为每个用户运行的合并语句。当合并为一个用户运行的同时为整个数据库运行合并并且存在更新时,我们很少会遇到死锁情况。有没有办法阻止这种情况的发生,因为我希望一个人完成然后释放锁然后下一个完成,找不到要更新的数据并继续。任何帮助将不胜感激。

<deadlock>
  <victim-list>
    <victimProcess id="processe7058f0c8" />
  </victim-list>
  <process-list>
    <process id="processe7058f0c8" taskpriority="0" logused="344" waitresource="KEY: 7:72057600589430784 (dc897b8904f5)" waittime="1341" ownerId="62095831" transactionname="user_transaction" lasttranstarted="2015-02-06T16:29:17.037" XDES="0xe789ce3a8" lockMode="U" schedulerid="6" kpid="4072" status="suspended" spid="59" sbid="2" ecid="0" priority="0" trancount="2" lastbatchstarted="2015-02-06T16:29:17.037" lastbatchcompleted="2015-02-06T16:29:17.033" lastattention="1900-01-01T00:00:00.033" clientapp=".Net SqlClient Data Provider" hostname="ZNPRODWEB1" hostpid="22512" loginname="DATABASE" isolationlevel="read committed (2)" xactid="62095831" currentdb="7" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
      <executionStack>
        <frame procname="DATABASE.Store.UpdateStoredPublishingSegmentUsersForUsers" line="20" stmtstart="794" stmtend="3468" sqlhandle="0x03000700d8b7073ae34ef00037a4000001000000000000000000000000000000000000000000000000000000">
MERGE [Store].StoredPublishingSegmentUsers AS TARGET
      USING 
      (
          SELECT realTimePublishings.PublishingSegmentId, 
                 realTimePublishings.UserId, 
                 realTimePublishings.IsParticipating,
                 realTimePublishings.IsVisibleWithFilter
            FROM Publishing.PublishingSegmentUsersRealTimeData realTimePublishings
           INNER JOIN @UserIds u ON u.Id = realTimePublishings.UserId
      ) AS SOURCE
      ON TARGET.PublishingSegments_PublishingSegmentId = SOURCE.PublishingSegmentId 
      AND TARGET.Users_UserId = SOURCE.UserId 
      WHEN MATCHED AND (TARGET.IsParticipating != SOURCE.IsParticipating) 
                    OR (TARGET.IsVisibleWithFilter != SOURCE.IsVisibleWithFilter) THEN
        UPDATE SET TARGET.IsParticipating = SOURCE.IsParticipating,
                   TARGET.IsVisibleWithFilter = SOURCE.IsVisibleWithFilter
      WHEN NOT MATCHED BY TARGET THEN
        INSERT ([PublishingSegments_PublishingSegmentId], [Users_UserId    </frame>
        <frame procname="DATABASE.Store.ProcessStoreUpdatesForCustomer" line="51" stmtstart="2844" stmtend="3082" sqlhandle="0x030007008e48614a974ff00037a4000001000000000000000000000000000000000000000000000000000000">
EXEC Store.UpdateStoredPublishingSegmentUsersForUsers @UserIds

         -- Update the stored incentive program leagues    </frame>
      </executionStack>
      <inputbuf>
Proc [Database Id = 7 Object Id = 1247889550]   </inputbuf>
    </process>
    <process id="processe7f0450c8" taskpriority="0" logused="352" waitresource="PAGE: 7:1:42475 " waittime="1272" ownerId="62094952" transactionname="user_transaction" lasttranstarted="2015-02-06T16:29:15.450" XDES="0xe6e6c63a8" lockMode="U" schedulerid="7" kpid="1260" status="suspended" spid="67" sbid="2" ecid="0" priority="0" trancount="2" lastbatchstarted="2015-02-06T16:29:15.450" lastbatchcompleted="2015-02-06T16:29:10.670" lastattention="1900-01-01T00:00:00.670" clientapp=".Net SqlClient Data Provider" hostname="ZNPRODWEB1" hostpid="19952" loginname="DATABASE" isolationlevel="read committed (2)" xactid="62094952" currentdb="7" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
      <executionStack>
        <frame procname="DATABASE.Store.UpdateStoredPublishingSegmentUsersForUsers" line="20" stmtstart="794" stmtend="3468" sqlhandle="0x03000700d8b7073ae34ef00037a4000001000000000000000000000000000000000000000000000000000000">
MERGE [Store].StoredPublishingSegmentUsers AS TARGET
      USING 
      (
          SELECT realTimePublishings.PublishingSegmentId, 
                 realTimePublishings.UserId, 
                 realTimePublishings.IsParticipating,
                 realTimePublishings.IsVisibleWithFilter
            FROM Publishing.PublishingSegmentUsersRealTimeData realTimePublishings
           INNER JOIN @UserIds u ON u.Id = realTimePublishings.UserId
      ) AS SOURCE
      ON TARGET.PublishingSegments_PublishingSegmentId = SOURCE.PublishingSegmentId 
      AND TARGET.Users_UserId = SOURCE.UserId 
      WHEN MATCHED AND (TARGET.IsParticipating != SOURCE.IsParticipating) 
                    OR (TARGET.IsVisibleWithFilter != SOURCE.IsVisibleWithFilter) THEN
        UPDATE SET TARGET.IsParticipating = SOURCE.IsParticipating,
                   TARGET.IsVisibleWithFilter = SOURCE.IsVisibleWithFilter
      WHEN NOT MATCHED BY TARGET THEN
        INSERT ([PublishingSegments_PublishingSegmentId], [Users_UserId    </frame>
        <frame procname="DATABASE.Store.UpdateStoredPublishingSegmentUsersForUser" line="9" stmtstart="352" stmtend="484" sqlhandle="0x0300070011dcfb3af84ef00037a4000001000000000000000000000000000000000000000000000000000000">
EXEC Store.UpdateStoredPublishingSegmentUsersForUsers @UserIds;    </frame>
        <frame procname="DATABASE.Store.ProcessStoreUpdatesForUser" line="35" stmtstart="1814" stmtend="2048" sqlhandle="0x03000700832bce510e50f00037a4000001000000000000000000000000000000000000000000000000000000">
EXEC Store.UpdateStoredPublishingSegmentUsersForUser @UserId

         -- Update the stored incentive program leagues    </frame>
      </executionStack>
      <inputbuf>
Proc [Database Id = 7 Object Id = 1372466051]   </inputbuf>
    </process>
  </process-list>
  <resource-list>
    <keylock hobtid="72057600589430784" dbid="7" objectname="DATABASE.Store.StoredPublishingSegmentUsers" indexname="PK_StoredPublishingSegmentUsers" id="locke68752b80" mode="U" associatedObjectId="72057600589430784">
      <owner-list>
        <owner id="processe7f0450c8" mode="U" />
      </owner-list>
      <waiter-list>
        <waiter id="processe7058f0c8" mode="U" requestType="wait" />
      </waiter-list>
    </keylock>
    <pagelock fileid="1" pageid="42475" dbid="7" subresource="FULL" objectname="DATABASE.Store.StoredPublishingSegmentUsers" id="locke64335280" mode="IU" associatedObjectId="72057600589430784">
      <owner-list>
        <owner id="processe7058f0c8" mode="IU" />
      </owner-list>
      <waiter-list>
        <waiter id="processe7f0450c8" mode="U" requestType="convert" />
      </waiter-list>
    </pagelock>
  </resource-list>
</deadlock>

这听起来像是一个难以解决的僵局。通常,可以通过确保一致的锁定顺序并将锁定模式增加到 S 以上来解决这个问题。目前还不清楚这将如何在这里完成。

建议:

  1. 你能容忍(罕见的)后台更新 U 锁 table 吗?这将允许读取同时发生,但它会锁定所有其他更新。使用:WITH (TABLOCK, UPDLOCK).
  2. 将罕见的后台更新设置 DEADLOCK_PRIORITYLOW 并将其置于重试循环中。重试是解决死锁问题的有效方法。
  3. 死锁是由于一个传输锁定行,另一个锁定页面。您 可能 可以通过使两个语句都使用 ROWLOCK 来解决这个问题。我没有这方面的经验,所以这只是一个猜测。

所有这三个都应该引起最小的代码更改。

我可能更喜欢 (2),因为它易于理解、简单且明确正确。