데드락 추적하기
SQL 서버에서 데드락이 걸리면 락 매니저는 그 중 하나를 victim 으로 삼아서 죽이게 된다. 문제는 기본 설정 만으로는 어떤 SQL문끼리의 충돌인지를 확인할 수 없다는 점인데, SQL Server 에러 로그를 이용해서 이를 쉽게 알아낼 수 있다.
normal deadlock
dbcc traceon(1204,3605,-1) 를 이용하면, 데드락 이벤트(1204)가 발생할 경우 에러 로그에 남길 수 있다. -1 은 세션 설정이 아닌 서버 전역 설정을 의미한다. 가장 좋은 방법은 시스템 시작 파라미터에다가 -T1204 를 넘겨주는 방식. 단 성능에 문제가 있을 수 있으므로, 데드락을 잡고 나서 끄는 것을 잊지 말도록.
주기적으로 sp_readerrorlog 또는 엔터프라이즈 매니저에서 확인해보다 보면 아래와 같은 로그가 나올 때가 있다.
Deadlock encountered .... Printing deadlock information 0
2006-12-13 14:31:13.11 spid4 0
2006-12-13 14:31:13.11 spid4 Wait-for graph 0
2006-12-13 14:31:13.11 spid4 0
2006-12-13 14:31:13.11 spid4 Node:1 0
2006-12-13 14:31:13.11 spid4 RID: 8:1:3534:228 CleanCnt:1 Mode: X Flags: 0x2 0
2006-12-13 14:31:13.11 spid4 Grant List 2:: 0
2006-12-13 14:31:13.11 spid4 Owner:0x4ae74ca0 Mode: X Flg:0x0 Ref:0 Life:02000000 SPID:64 ECID:0 0
2006-12-13 14:31:13.11 spid4 SPID: 64 ECID: 0 Statement Type: UPDATE Line #: 12 0
2006-12-13 14:31:13.11 spid4 Input Buf: Language Event: insert into take_time_table (type, name, userID, takeTime) values (4, 'PickupServerApp', 50780, 6493) 0
2006-12-13 14:31:13.11 spid4 Requested By: 0
2006-12-13 14:31:13.11 spid4 ResType:LockOwner Stype:'OR' Mode: S SPID:58 ECID:0 Ec:(0x778C9508) Value:0x7321ac20 Cost:(0/1BC) 0
2006-12-13 14:31:13.11 spid4 0
2006-12-13 14:31:13.11 spid4 Node:2 0
2006-12-13 14:31:13.11 spid4 RID: 8:1:3535:117 CleanCnt:1 Mode: X Flags: 0x2 0
2006-12-13 14:31:13.11 spid4 Grant List 1:: 0
2006-12-13 14:31:13.11 spid4 Owner:0x3e54c7c0 Mode: X Flg:0x0 Ref:0 Life:02000000 SPID:58 ECID:0 0
2006-12-13 14:31:13.11 spid4 SPID: 58 ECID: 0 Statement Type: CONDITIONAL Line #: 8 0
2006-12-13 14:31:13.11 spid4 Input Buf: Language Event: insert into take_time_table (type, name, userID, takeTime) values (2, 'GatewayServerApp', 50977, 0) 0
2006-12-13 14:31:13.11 spid4 Requested By: 0
2006-12-13 14:31:13.11 spid4 ResType:LockOwner Stype:'OR' Mode: U SPID:64 ECID:0 Ec:(0x611CD508) Value:0x4ae74e20 Cost:(0/134) 0
2006-12-13 14:31:13.11 spid4 Victim Resource Owner: 0
2006-12-13 14:31:13.11 spid4 ResType:LockOwner Stype:'OR' Mode: U SPID:64 ECID:0 Ec:(0x611CD508) Value:0x4ae74e20 Cost:(0/134) 0
위 로그에 의하면 single insert statement 2개를 동시에 실행할 때 데드락이 걸렸다. 처음에는 legacy 서버에서 스토어드 프로시저 대신 raw SQL을 실행했기 때문에 발생된 것으로 추측했는데, 논리적으로 이 둘이 데드락을 유발시키기는 좀 힘든 상황이었다. 이 경우 dbcc page 명령을 이용하면, 구체적으로 어떤 메모리 페이지에서 락이 걸렸는지를 확인해볼 수 있다. 위 로그에서 RID 부분을 찾아서 넘겨주면 끝. (단 이 명령을 실행하면 역시나 에러 로그에 남기 때문에, 나중에 보고 놀래지 말 것!)
[sql]
dbcc page(8,1,3534,3)
dbcc page(8,1,3535,3)
[/sql]
결국 문제는 저 테이블에는 다른 테이블로 또다시 select & insert 하는 무지몽매한 트리거(-_-)가 걸려 있어서 생긴 문제였다. 하아.
Key deadlock
이번엔 키 데드락 로그를 살펴보자. 이 경우 RID 대신 KEY 라고 나오는데, SELECT OBJECT_NAME(KeyID) 를 이용해서 어떤 오브젝트에서 락이 걸렸는지를 알아낼 수 있다.
Deadlock encountered .... Printing deadlock information 0
2006-12-20 20:37:35.73 spid4 0
2006-12-20 20:37:35.73 spid4 Wait-for graph 0
2006-12-20 20:37:35.73 spid4 0
2006-12-20 20:37:35.73 spid4 Node:1 0
2006-12-20 20:37:35.73 spid4 KEY: 8:612197231:1 (5000dcb808b7) CleanCnt:1 Mode: Range-S-U Flags: 0x0 0
2006-12-20 20:37:35.73 spid4 Grant List 1:: 0
2006-12-20 20:37:35.73 spid4 Grant List 3:: 0
2006-12-20 20:37:35.73 spid4 Owner:0x25bdf860 Mode: Range-S-S Flg:0x0 Ref:0 Life:02000000 SPID:69 ECID:0 0
2006-12-20 20:37:35.73 spid4 SPID: 69 ECID: 0 Statement Type: EXECUTE Line #: 0 0
2006-12-20 20:37:35.73 spid4 Input Buf: RPC Event: sp_cursoropen;1 0
2006-12-20 20:37:35.73 spid4 Requested By: 0
2006-12-20 20:37:35.73 spid4 ResType:LockOwner Stype:'OR' Mode: X SPID:57 ECID:0 Ec:(0x39939508) Value:0x5223ee00 Cost:(0/1070) 0
2006-12-20 20:37:35.73 spid4 0
2006-12-20 20:37:35.73 spid4 Node:2 0
2006-12-20 20:37:35.73 spid4 KEY: 8:612197231:1 (d000bebebbe4) CleanCnt:2 Mode: X Flags: 0x0 0
2006-12-20 20:37:35.73 spid4 Wait List: 0
2006-12-20 20:37:35.73 spid4 Owner:0x3dc49b20 Mode: S Flg:0x0 Ref:1 Life:00000000 SPID:65 ECID:0 0
2006-12-20 20:37:35.73 spid4 SPID: 65 ECID: 0 Statement Type: SELECT Line #: 1 0
2006-12-20 20:37:35.73 spid4 Input Buf: Language Event: select b.charid from user_table a join char_table b on a.userid=b.userid where b.disable=0 and b.usertype=2 and a.username='throwrocks@me.com' 0
2006-12-20 20:37:35.73 spid4 Requested By: 0
2006-12-20 20:37:35.73 spid4 ResType:LockOwner Stype:'OR' Mode: Range-S-S SPID:69 ECID:0 Ec:(0x3A7A1548) Value:0x68dded80 Cost:(0/0) 0
2006-12-20 20:37:35.73 spid4 0
2006-12-20 20:37:35.73 spid4 Node:3 0
2006-12-20 20:37:35.73 spid4 KEY: 8:612197231:1 (d000bebebbe4) CleanCnt:2 Mode: X Flags: 0x0 0
2006-12-20 20:37:35.73 spid4 Grant List 1:: 0
2006-12-20 20:37:35.73 spid4 Owner:0x4f336b40 Mode: X Flg:0x0 Ref:0 Life:02000000 SPID:57 ECID:0 0
2006-12-20 20:37:35.73 spid4 SPID: 57 ECID: 0 Statement Type: UPDATE Line #: 135 0
2006-12-20 20:37:35.73 spid4 Input Buf: RPC Event: U_CharacterSaveLocker;1 0
2006-12-20 20:37:35.73 spid4 Requested By: 0
2006-12-20 20:37:35.73 spid4 ResType:LockOwner Stype:'OR' Mode: S SPID:65 ECID:0 Ec:(0x74FA7548) Value:0x3dc49b20 Cost:(0/0) 0
2006-12-20 20:37:35.73 spid4 Victim Resource Owner: 0
2006-12-20 20:37:35.73 spid4 ResType:LockOwner Stype:'OR' Mode: S SPID:65 ECID:0 Ec:(0x74FA7548) Value:0x3dc49b20 Cost:(0/0) 0
2006-12-20 20:37:40.73 spid4 0
DBCC PSS
DBCC PSS(uid [, spid]) 를 통해서 특정 pid 의 다양한 속성들을 알 수 있다. uid 는 select * from sysusers 로 체크하고, 프로세스는 sp_who2 로 알아내면 된다. 물론 spid 를 생략하면 해당 유저의 모든 프로세스의 정보가 뜬다.
PSS @0x1B9E71E0
---------------
pspid = 53 m_dwLoginFlags = 0x03e0 plsid = 1827921760
pbackground = 0
pbSid
-----
81ee3246 10030a4a 8c19c980 f40a127f ..2F...J........
sSecFlags = 0x2 pdeadlockpri = 0 poffsets = 0x0
pss_stats = 0x0 ptickcount = 905625531
pcputickcount = 2711581261636168 ploginstamp = 313917
ptimestamp = 2006-12-18 03:18:26.550 plangid = 0
pdateformat = 1 pdatefirst = 7 Language = us_english
UserName = ubo_game poptions = 0x28400020 poptions2 = 0x3f438
pline = 1 pcurstepno = 0 prowcount = 1
pstatlist = 0 pcurcmd = 253 pseqstat = 0
ptextsize = 2147483647 pretstat = 0 CNestLevel = 0
@@procid = 0 pslastbatchstarttime = 2006-12-21 23:36:14.363
pmemusage = -2095 hLicense = 0 tpFlags = 0x1
isolation_level = 0 fips_flag = 0x0 sSaveSecFlags = 0x0
psavedb = 0 pfetchstat = 0 pcrsrows = 0
pslastbatchtime = 2006-12-21 23:36:14.363 pubexecdb = 0
fInReplicatedProcExec = 0 pMsqlXact = 0x6D6AD038 presSemCount = [0]0
presSemCount = [0]0 pcputot = 5182694 pcputotstart = 5182694
pcpucmdstart = 5182694 pbufread = 346 pbufreadstart = 346
plogbufread = 11300361 plogbufreadstart = 11300354
pbufwrite = 6374 pbufwritestart = 6374 pLockTimeout = 4294967295
pUtilResultSet = 0x00000000
[sql]
SET SHOWPLAN_ALL ON
SET STATISTICS PROFILE ON
[/sql]
DB에 문제가 생기면 언제나 무료로 컨설팅해주시는 자칭 초보 DBA 창아군에게 Special Thanks를 보낸다.