2010-05-31 5 views
5

큰 웹 응용 프로그램에서 많은 교착 상태가 발생합니다. 교착 상태에 대한 데이터가 있지만 그 이유를 이해할 수 없습니다.

How to automatically re-run deadlocked transaction? (ASP.NET MVC/SQL Server)

은 여기 다시 실행 교착 거래를 원했지만은 교착 상태를 제거하는 말을 들었다 - 그것은 교착 상태를 잡으려고 노력보다 훨씬 낫다.

그래서 저는 하루 종일 SQL 프로파일 러를 사용하여 추적 키 등을 설정했습니다. 그리고 이것이 제가 가진 것입니다.

Users 테이블이 있습니다.

User = DB.Users.SingleOrDefault(u => u.Password == password && u.Name == username); 
: 나는 (그것이 유일한 쿼리 아니지만,이 문제의 원인이되는 하나)

UPDATE Users 
SET views = views + 1 
WHERE ID IN (SELECT AuthorID FROM Articles WHERE ArticleID = @ArticleID) 

그리고 에서 다음 쿼리 ALL 페이지가있어 다음 쿼리와 매우 높은 사용 가능한 페이지가

쿠키에서 사용자를 얻는 곳입니다.

매우 자주 교착 상태가 발생하고이 두 번째 Linq-to-SQL 쿼리가 피해자로 선택되어 실행되지 않으며 내 사이트 사용자에게 오류 화면이 표시됩니다.

이 SQL 프로파일에 의해 캡처 된 .XDL 그래프의 정보입니다 (그것은 하나의 단지 처음 교착 아니다있어 전체 목록은 거대한입니다..) : 나는 교착 상태에 대해 많이 읽어

<deadlock-list> 
    <deadlock victim="process824df048"> 
     <process-list> 
      <process id="process824df048" taskpriority="0" logused="0" waitresource="PAGE: 7:1:13921" waittime="1830" ownerId="91418" transactionname="SELECT" lasttranstarted="2010-05-31T12:17:37.663" XDES="0x868175e0" lockMode="S" schedulerid="2" kpid="5076" status="suspended" spid="72" sbid="0" ecid="2" priority="0" trancount="0" lastbatchstarted="2010-05-31T12:17:37.663" lastbatchcompleted="2010-05-31T12:17:37.663" clientapp=".Net SqlClient Data Provider" hostname="WIN-S41KV2CLS67" hostpid="6920" isolationlevel="read committed (2)" xactid="91418" currentdb="7" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056"> 
       <executionStack> 
        <frame procname="adhoc" line="1" stmtstart="74" sqlhandle="0x02000000de1cb30b5b2e40e31ffb345af3c7529430b559c2"> 
*password-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------  </frame> 
        <frame procname="unknown" line="1" sqlhandle="0x000000000000000000000000000000000000000000000000"> 
unknown  </frame> 
       </executionStack> 
       <inputbuf> 
       </inputbuf> 
      </process> 
      <process id="process8765fb88" taskpriority="0" logused="216" waitresource="PAGE: 7:1:14196" waittime="1822" ownerId="91408" transactionname="UPDATE" lasttranstarted="2010-05-31T12:17:37.640" XDES="0x86978e90" lockMode="IX" schedulerid="2" kpid="5216" status="suspended" spid="73" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2010-05-31T12:17:37.557" lastbatchcompleted="2010-05-31T12:17:37.557" clientapp=".Net SqlClient Data Provider" hostname="WIN-S41KV2CLS67" hostpid="6920" loginname="sdfkj93jks9sl" isolationlevel="read committed (2)" xactid="91408" currentdb="7" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056"> 
       <executionStack> 
        <frame procname="database.dbo.UpdateUserStats" line="31" stmtstart="1794" stmtend="2088" sqlhandle="0x03000700bac8836333e58f00879d00000100000000000000"> 
UPDATE Users 
    SET Views = Views + 1 
    WHERE ID IN (SELECT AuthorID FROM Articles WHERE ArticleID = @ArticleID)  </frame> 
        <frame procname="adhoc" line="1" stmtstart="84" sqlhandle="0x01000700b7c78e0760dd3f81000000000000000000000000"> 
EXEC @RETURN_VALUE = [dbo].[UpdateUserStats] @UserID = @p0 </frame> 
        <frame procname="unknown" line="1" sqlhandle="0x000000000000000000000000000000000000000000000000"> 
unknown  </frame> 
       </executionStack> 
       <inputbuf> 
(@p0 int,@RETURN_VALUE int output)EXEC @RETURN_VALUE = [dbo].[UpdateUserStats] @UserID = @p0 </inputbuf> 
      </process> 
      <process id="process86ce0988" taskpriority="0" logused="10000" waittime="1806" schedulerid="1" kpid="2604" status="suspended" spid="72" sbid="0" ecid="0" priority="0" trancount="0" lastbatchstarted="2010-05-31T12:17:37.663" lastbatchcompleted="2010-05-31T12:17:37.663" clientapp=".Net SqlClient Data Provider" hostname="WIN-S41KV2CLS67" hostpid="6920" loginname="sdfkj93jks9sl" isolationlevel="read committed (2)" xactid="91418" currentdb="7" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056"> 
       <executionStack> 
        <frame procname="adhoc" line="1" stmtstart="74" sqlhandle="0x02000000de1cb30b5b2e40e31ffb345af3c7529430b559c2"> 
*password-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------  </frame> 
        <frame procname="unknown" line="1" sqlhandle="0x000000000000000000000000000000000000000000000000"> 
unknown  </frame> 
       </executionStack> 
       <inputbuf> 
*password-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- </inputbuf> 
      </process> 
     </process-list> 
     <resource-list> 
      <pagelock fileid="1" pageid="13921" dbid="7" objectname="database.dbo.Users" id="lock85535c80" mode="IX" associatedObjectId="72057594046382080"> 
       <owner-list> 
        <owner id="process8765fb88" mode="IX"/> 
       </owner-list> 
       <waiter-list> 
        <waiter id="process824df048" mode="S" requestType="wait"/> 
       </waiter-list> 
      </pagelock> 
      <pagelock fileid="1" pageid="14196" dbid="7" objectname="database.dbo.Users" id="lock8469f980" mode="SIU" associatedObjectId="72057594046382080"> 
       <owner-list> 
        <owner id="process86ce0988" mode="S"/> 
       </owner-list> 
       <waiter-list> 
        <waiter id="process8765fb88" mode="IX" requestType="convert"/> 
       </waiter-list> 
      </pagelock> 
      <exchangeEvent id="Pipe894b0680" WaitType="e_waitPipeGetRow" nodeId="0"> 
       <owner-list> 
        <owner id="process824df048"/> 
       </owner-list> 
       <waiter-list> 
        <waiter id="process86ce0988"/> 
       </waiter-list> 
      </exchangeEvent> 
     </resource-list> 
    </deadlock> 

... 그리고 이것이 왜 교착 상태를 일으키는 지 이해할 수 없습니다.

분명히 두 쿼리 모두 매우 자주 실행됩니다. 최소한 1 초에 1 번. 어쩌면 더 자주 (300-400 명의 사용자 온라인). 그래서 그들은 동시에 쉽게 실행될 수 있습니다. 그러나 이것이 교착 상태를 일으키는 이유는 무엇입니까? 도와주세요.

는 SQL2005에있는 경우

을 (읽기 쓰기 트랜잭션이 교착되고 만 트랜잭션을 읽기 Linq는 SQL에)

+1

SQL2005 이상이 있습니까? 그렇다면 SQL Profiler에서 교착 상태 그래프를 얻었습니까? 또한 쿼리가 실행중인 트랜잭션 격리 수준을 알고 있습니까? –

+0

예 그래프가 있습니다. 그것이 내가이 정보를 얻은 곳입니다. 나는 트랜잭션 격리에 대해 아무것도 모른다.어디에서 확인할 수 있습니까? – Alex

+1

프로파일 러 추적에서 교착 상태 그래프 이벤트를 마우스 오른쪽 단추로 클릭하고 이벤트 데이터 추출을 선택한 다음 xml로 저장하십시오. 그런 다음 메모장에서 열고 "isolationlevel"을 찾으십시오. –

답변

11

교착 상태 그래프를 캡처해야합니다. 프로필러를 연결하고 Deadlock Graph Event 클래스를 캡처하십시오. .XDL 그래프를 저장하고 해당 정보를 게시물에 추가하십시오.

그 때까지 DB.Users가 꽤 분명합니다.

CREATE INDEX idxUsersNamePassword on Users(Name,Password); 

나는 사용자가 이미 ID에 인덱스가 기대와 기사가 너무 AuthorID을 다룹은 articleID에 인덱스를 가지고 : SingleOrDefault 쿼리는 적어도이 아닌 경우 이름과 암호에 이름에 인덱스를 필요로한다. Users.ID 및 Articles.ArticleID가 각각 테이블에있는 PK라고 가정하면 해당 테이블은 아마도 각각의 클러스터 된 키일 수 있습니다. 두 번 확인해 볼 가치가 있습니다. 게다가

ALTER DATABASE ... SET READ_COMMITTED_SNAPSHOT ON 

을, 일반 텍스트 비밀번호가 저장 : 이미 이전의 게시물에 한 번을 답으로

그리고, 당신은 당신이 Snapshot Isolation 켜기 고려해야 유엔-대답에 이동 떠나기로 결정 주요한 #fail. 이다

  • A)를 SELECT ... FROM Users WHERE Password = ... and Name = ...
  • B를 실행 ... F048) ... 0988 :

    이 세 가지 프로세스 (요청)가 있습니다 업데이트 교착 상태 정보를 원하시면 후

    SELECT ... FROM Users WHERE Password = ... and Name = ...

  • C) ... 실행중인 FB88 ... UPDATE ...

교착 사이클이다

  1. 가의 S 페이지 S 잠금
  2. B 기다리는 잠금 C는 페이지 IX 로크를 대기 차단되고, C의 IX 로크에 의해 차단
  3. A는 평행 대기 교환 자원은 B에 의해 차단됩니다.

주기는 C-> A-> B-> C입니다.

관련된 두 SELECT가 1) 병렬 계획을 사용하고 2) 페이지 잠금을 사용한다는 것이 전체 사용자 테이블의 종단 간 검색을 수행한다는 것이 분명합니다. 그래서 문제는 내가 예상 한 바와 같이 쿼리를 너무 많은 데이터를 스캔하게하는 사용자의 (이름, 암호)에 대한 색인이 부족하다는 것입니다. 인덱스를 추가하면 Nc 인덱스에서 SELECT를 직선으로 선택하고 Clustered 인덱스에서 조회를하면 UPDATE와의 겹침 창이 크게 줄어 듭니다. 현재 UPDATE는 모두 SELECT와 충돌하는 것을 보장합니다. 모든 SELECT는 모든 행을 읽도록 보장되어 있기 때문입니다.

색인을 추가하면 즉각적인 문제를 해결할 수 있습니다. 스냅 샷 격리를 사용하면 마스크이 문제가됩니다. (이름, 암호) 색인이 추가되지 않으면 종단 간 검사가 계속 수행되기 때문입니다. 또는 (Name)만이 잘 작동합니다.

향후 확장 성을 위해 모든 페이지보기에서 보기 열을 업데이트하면이 업데이트되지 않습니다. 지연된 업데이트, 일괄 집계 횟수 업데이트, 수직적으로 사용자 테이블을 분할하고 Views 열을 가져 오는 것이 가능한 대안입니다.

+1

죄송합니다, 답변을 수락하는 것을 잊었습니다. 예, 암호 저장 시스템을 업데이트 할 예정입니다. 내 질문을 .XDL 정보로 업데이트합니다. – Alex

+0

.XDL 정보가 게시됩니다. – Alex

+0

이렇게 상세한 답변을 주셔서 대단히 감사합니다! 기본 키와 다른 다른 열을 인덱싱하지 못했습니다. (이름, 비밀번호) 색인을 추가하려면 어떻게해야합니까? 테이블 디자이너에서 색인/키 창을 발견했습니다. PK_Users 키가 있고 사용자 이름에 IX_Users 키를 추가 할 수는 있지만 비밀번호와 어떻게 연결할 수 있습니까? 다시 한 번 감사드립니다. – Alex

1

문제가 여기 Diagnosing Deadlocks in SQL Server 2005

와 평행선을 많이 가지고 주셔서 감사합니다 또는 나중에 해당 스레드에서 논의 된대로 스냅 숏 격리를 설정하면 작업이 수행됩니다. 그렇지 않으면 사용중인 버전의 세부 정보로 게시물을 업데이트하십시오.

+0

감사합니다. – Alex

1

이 상황 (즉, 읽고있는 데이터의 유형과 해당 데이터에서 발생하는 업데이트의 성격)은 커밋되지 않은 읽기 전용으로 사용자 조회 쿼리를 실행합니다.

또는 더 복잡한 변경. 게시 한 설명에서 사용자 기록에 조회수를 유지하지 않는 것이 좋습니다. 대신 기사에 대해 ViewCount를 기록한 다음 Author.ID별로 Articles.ViewCount의 합계에서 사용자에 대한 전체보기를 보류했습니다.

관련 문제