Πώς συλλέγουμε deadlocked queries μέσω Extended Event και πως διαβάζουμε τα δεδομένα του

- Πώς μπορούμε να αυξήσουμε την απόδοση στο target Replicat του Oracle GoldenGate με παραλληλία - 19 Μάρτιος 2025
- Πώς φτιάχνουμε χρήστες σε βάσεις δεδομένων που ανήκουν σε Always On Availability Group του SQL Server - 10 Φεβρουάριος 2025
- Πώς φτιάχνουμε Logon Trigger για να ελέγχουμε τους χρήστες που επιτρέπουμε να συνδεθούν σε Oracle Database - 13 Ιανουάριος 2025
Σε παλαιότερο άρθρο είχαμε δει πως δημιουργούμε Extended Event για να δούμε τα queries με μεγάλη διάρκεια. Σε αυτό το άρθρο θα φτιάξουμε Extended Event που καταγράφει τα deadlocks.
Το μόνο που χρειάζεται να κάνουμε είναι να το δημιουργήσουμε με ένα T-SQL command. Αφού δημιουργηθεί θα καταγράφει στο path που του έχουμε δηλώσει σε ένα αρχείο XML όσα queries είχανε διάρκεια πάνω από 1 δευτερόλεπτο.
Για την δημιουργία του Extended Event
Δεν πρέπει να ξεχάσουμε να αλλάξουμε το path σε αυτό που θα θέλουμε να αποθηκεύονται τα δεδομένα.
Έπειτα το εκτελούμε σε ένα απλό query window.
CREATE EVENT SESSION [deadLock] ON SERVER ADD EVENT sqlserver.xml_deadlock_report( ACTION(package0.callstack,package0.collect_cpu_cycle_time,package0.collect_current_thread_id,package0.collect_system_time,package0.event_sequence,package0.last_error,package0.process_id,sqlos.cpu_id,sqlos.scheduler_id,sqlserver.client_app_name,sqlserver.client_connection_id,sqlserver.client_hostname,sqlserver.client_pid,sqlserver.context_info,sqlserver.database_id,sqlserver.database_name,sqlserver.is_system,sqlserver.query_plan_hash,sqlserver.server_instance_name,sqlserver.server_principal_name,sqlserver.server_principal_sid,sqlserver.session_id,sqlserver.session_nt_username,sqlserver.session_server_principal_name,sqlserver.sql_text,sqlserver.transaction_id,sqlserver.tsql_frame,sqlserver.username)) ADD TARGET package0.event_file(SET filename=N'D:\xevents\deadlock.xel',max_file_size=(5)) WITH (MAX_MEMORY=4096 KB,EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS,MAX_DISPATCH_LATENCY=30 SECONDS,MAX_EVENT_SIZE=0 KB,MEMORY_PARTITION_MODE=NONE,TRACK_CAUSALITY=OFF,STARTUP_STATE=ON) GO alter event session deadLock on server state=start; go
Πως διαβάζουμε τα αποτελέσματα
Στο path που είχαμε ορίσει κατά την δημιουργία του XEvent δημιουργήθηκε ένα .xel και ένα .xem αρχείο. Μέσω του dynamic view sys.fn_xe_file_target_read_file με παραμέτρους αυτά τα αρχεία έχουμε ως αποτέλεσμα όλο το XML αρχείο με την πληροφορία.
Όμως επειδή δεν είναι πρακτικό να διαβάζεις με το μάτι ένα XML πρέπει κάπως να φιλτραριστεί το αρχείο σε μια πιο ευανάγνωστη μορφή.
Οπότε έκατσα και έφτιαξα ένα query που κάνει αυτή τη δουλειά
Για αρχή μέσω του ονόματος που φτιάξαμε το Xevent βρίσκει το path που βρίσκεται δυναμικά και το ορίζει σαν παράμετρο στο query.
Έπειτα ορίζουμε το XML σαν ένα Common Table Expression. Κάνοντας το SELECT μπορούμε να φέρουμε την τιμή από την κάθε ιδιότητα που χρειαζόμαστε.
Στο where statement μπορούμε να φιλτράρουμε συγκεκριμένες βάσεις, την διάρκεια και το χρονικό διάστημα που εκτελέστηκε.
Σε αυτό το κομμάτι θέλει προσοχή, καθώς το default xml timestamp είναι σε UTC timezone οπότε για να βάζουμε την πραγματική ώρα που έχει ο server προσθέτουμε την διαφορά ώρας που έχει ο server με το UTC timezone (DATEADD(HOUR,DATEDIFF(hour, SYSUTCDATETIME(),SYSDATETIME() )).
declare @xel as varchar(max), @xem as varchar(max) set @xel=(select --n.name as XeventName, SUBSTRING(cast(f.value as varchar(max)),1,len(cast(f.value as varchar(max)))-4)+'*.xel' as xel --,SUBSTRING(cast(f.value as varchar(max)),1,len(cast(f.value as varchar(max)))-4)+'*.xem' as xem from sys.server_event_session_fields f inner join sys.server_event_sessions n on f.event_session_id = n.event_session_id WHERE f.NAME = 'filename' and n.name like '%deadLock%') --Set xevent Name set @xem=(select --n.name as XeventName, --SUBSTRING(cast(f.value as varchar(max)),1,len(cast(f.value as varchar(max)))-4)+'*.xel' as xel SUBSTRING(cast(f.value as varchar(max)),1,len(cast(f.value as varchar(max)))-4)+'*.xem' as xem from sys.server_event_session_fields f inner join sys.server_event_sessions n on f.event_session_id = n.event_session_id WHERE f.NAME = 'filename' and n.name like '%deadLock%') --Set xevent Name ;WITH XEvents AS ( select object_name, CAST(event_data AS XML) AS A FROM sys.fn_xe_file_target_read_file( @xel ,@xem , NULL, NULL) ) SELECT --object_name AS EventName, A.value ('(/event/action[@name=''database_name'']/value)[1]', 'VARCHAR(MAX)') AS DB_Name, DATEADD(HOUR,DATEDIFF(hour, SYSUTCDATETIME(),SYSDATETIME() ),A.value ('(/event/@timestamp)[1]', 'DATETIME')) AS [Time], --A.value ('(/event/data/value/deadlock/resource-list/objectlock/@dbid)[1]', 'VARCHAR(MAX)') AS dbid, d.name as DB_Name, A.value ('(/event/data/value/deadlock/resource-list/objectlock/@objectname)[1]', 'VARCHAR(MAX)') AS locked_objectname, A.value ('(/event/data[@name=''xml_report'']/value)[1]', 'VARCHAR(MAX)') AS All_Queries, A.value ('(/event/data/value/deadlock/victim-list/victimProcess/@id)[1]', 'VARCHAR(MAX)') AS VictimProcess1, A.value ('(/event/data/value/deadlock/victim-list/victimProcess/@id)[2]', 'VARCHAR(MAX)') AS VictimProcess2, A.value ('(/event/data/value/deadlock/victim-list/victimProcess/@id)[3]', 'VARCHAR(MAX)') AS VictimProcess3, A.value ('(/event/data/value/deadlock/victim-list/victimProcess/@id)[4]', 'VARCHAR(MAX)') AS VictimProcess4, A.value ('(/event/data/value/deadlock/victim-list/victimProcess/@id)[5]', 'VARCHAR(MAX)') AS VictimProcess5, A.value ('(/event/data/value/deadlock/process-list/process/@id)[1]', 'VARCHAR(MAX)') AS ListProcess1, A.value ('(/event/data/value/deadlock/process-list/process/inputbuf)[1]', 'VARCHAR(MAX)') AS ListQuery1, A.value ('(/event/data/value/deadlock/process-list/process/@hostname)[1]', 'VARCHAR(MAX)') AS hostname1, A.value ('(/event/data/value/deadlock/process-list/process/@currentdbname)[1]', 'VARCHAR(MAX)') AS Currentdbname1, A.value ('(/event/data/value/deadlock/process-list/process/@loginname)[1]', 'VARCHAR(MAX)') AS Loginname1, A.value ('(/event/data/value/deadlock/process-list/process/@lockMode)[1]', 'VARCHAR(MAX)') AS lockMode1, A.value ('(/event/data/value/deadlock/process-list/process/@isolationlevel)[1]', 'VARCHAR(MAX)') AS isolationlevel1, A.value ('(/event/data/value/deadlock/process-list/process/executionStack/frame/@sqlhandle)[1]', 'VARCHAR(MAX)') AS sql_handle1, sf1.text as full_text1, qp1.query_plan as query_plan1, A.value ('(/event/data/value/deadlock/process-list/process/@id)[2]', 'VARCHAR(MAX)') AS ListProcess2, A.value ('(/event/data/value/deadlock/process-list/process/inputbuf)[2]', 'VARCHAR(MAX)') AS ListQuery2, A.value ('(/event/data/value/deadlock/process-list/process/@hostname)[2]', 'VARCHAR(MAX)') AS hostname2, A.value ('(/event/data/value/deadlock/process-list/process/@currentdbname)[2]', 'VARCHAR(MAX)') AS Currentdbname2, A.value ('(/event/data/value/deadlock/process-list/process/@loginname)[2]', 'VARCHAR(MAX)') AS Loginname2, A.value ('(/event/data/value/deadlock/process-list/process/@isolationlevel)[2]', 'VARCHAR(MAX)') AS isolationlevel12, A.value ('(/event/data/value/deadlock/process-list/process/@lockMode)[2]', 'VARCHAR(MAX)') AS lockMode2, A.value ('(/event/data/value/deadlock/process-list/process[2]/executionStack/frame/@sqlhandle)[1]', 'VARCHAR(MAX)') AS sql_handle2, sf2.text as full_text2, qp2.query_plan as query_plan2, A.value ('(/event/data/value/deadlock/process-list/process/@id)[3]', 'VARCHAR(MAX)') AS ListProcess3, A.value ('(/event/data/value/deadlock/process-list/process/inputbuf)[3]', 'VARCHAR(MAX)') AS ListQuery3, A.value ('(/event/data/value/deadlock/process-list/process/@lockMode)[3]', 'VARCHAR(MAX)') AS lockMode3, A.value ('(/event/data/value/deadlock/process-list/process/@hostname)[3]', 'VARCHAR(MAX)') AS hostname3, A.value ('(/event/data/value/deadlock/process-list/process/@currentdbname)[3]', 'VARCHAR(MAX)') AS Currentdbname3, A.value ('(/event/data/value/deadlock/process-list/process/@loginname)[3]', 'VARCHAR(MAX)') AS Loginname3, A.value ('(/event/data/value/deadlock/process-list/process/@isolationlevel)[3]', 'VARCHAR(MAX)') AS isolationlevel3, A.value ('(/event/data/value/deadlock/process-list/process[3]/executionStack/frame/@sqlhandle)[1]', 'VARCHAR(MAX)') AS sql_handle3, sf3.text as full_text3, qp3.query_plan as query_plan3, A.value ('(/event/data/value/deadlock/process-list/process/@id)[4]', 'VARCHAR(MAX)') AS ListProcess4, A.value ('(/event/data/value/deadlock/process-list/process/inputbuf)[4]', 'VARCHAR(MAX)') AS ListQuery4, A.value ('(/event/data/value/deadlock/process-list/process/@lockMode)[4]', 'VARCHAR(MAX)') AS lockMode4, A.value ('(/event/data/value/deadlock/process-list/process/@hostname)[4]', 'VARCHAR(MAX)') AS hostname4, A.value ('(/event/data/value/deadlock/process-list/process/@currentdbname)[4]', 'VARCHAR(MAX)') AS Currentdbname4, A.value ('(/event/data/value/deadlock/process-list/process/@loginname)[4]', 'VARCHAR(MAX)') AS Loginname4, A.value ('(/event/data/value/deadlock/process-list/process/@isolationlevel)[4]', 'VARCHAR(MAX)') AS isolationlevel4, A.value ('(/event/data/value/deadlock/process-list/process[4]/executionStack/frame/@sqlhandle)[1]', 'VARCHAR(MAX)') AS sql_handle4, sf4.text as full_text4, qp4.query_plan as query_plan4, A.value ('(/event/data/value/deadlock/process-list/process/@id)[5]', 'VARCHAR(MAX)') AS ListProcess5, A.value ('(/event/data/value/deadlock/process-list/process/inputbuf)[5]', 'VARCHAR(MAX)') AS ListQuery5, A.value ('(/event/data/value/deadlock/process-list/process/@lockMode)[5]', 'VARCHAR(MAX)') AS lockMode5, A.value ('(/event/data/value/deadlock/process-list/process/@hostname)[5]', 'VARCHAR(MAX)') AS hostname5, A.value ('(/event/data/value/deadlock/process-list/process/@loginname)[5]', 'VARCHAR(MAX)') AS Loginname5, A.value ('(/event/data/value/deadlock/process-list/process/@isolationlevel)[5]', 'VARCHAR(MAX)') AS isolationlevel5, A.value ('(/event/data/value/deadlock/process-list/process[5]/executionStack/frame/@sqlhandle)[1]', 'VARCHAR(MAX)') AS sql_handle5, sf5.text as full_text5, qp5.query_plan as query_plan5, A.value ('(/event/data/value/deadlock/process-list/process/@id)[6]', 'VARCHAR(MAX)') AS ListProcess6, A.value ('(/event/data/value/deadlock/process-list/process/inputbuf)[6]', 'VARCHAR(MAX)') AS ListQuery6, A.value ('(/event/data/value/deadlock/process-list/process/@lockMode)[6]', 'VARCHAR(MAX)') AS lockMode6, A.value ('(/event/data/value/deadlock/process-list/process/@hostname)[6]', 'VARCHAR(MAX)') AS hostname6, A.value ('(/event/data/value/deadlock/process-list/process/@loginname)[6]', 'VARCHAR(MAX)') AS Loginname6, A.value ('(/event/data/value/deadlock/process-list/process/@currentdbname)[6]', 'VARCHAR(MAX)') AS Currentdbname6, A.value ('(/event/data/value/deadlock/process-list/process/@isolationlevel)[6]', 'VARCHAR(MAX)') AS isolationlevel6, A.value ('(/event/data/value/deadlock/process-list/process[6]/executionStack/frame/@sqlhandle)[1]', 'VARCHAR(MAX)') AS sql_handle6, sf6.text as full_text6, qp6.query_plan as query_plan6, A.value ('(/event/data/value/deadlock/process-list/process/@id)[7]', 'VARCHAR(MAX)') AS ListProcess7, A.value ('(/event/data/value/deadlock/process-list/process/inputbuf)[7]', 'VARCHAR(MAX)') AS ListQuery7, A.value ('(/event/data/value/deadlock/process-list/process/@lockMode)[7]', 'VARCHAR(MAX)') AS lockMode7, A.value ('(/event/data/value/deadlock/process-list/process/@hostname)[7]', 'VARCHAR(MAX)') AS hostname7, A.value ('(/event/data/value/deadlock/process-list/process/@loginname)[7]', 'VARCHAR(MAX)') AS Loginname7, A.value ('(/event/data/value/deadlock/process-list/process/@currentdbname)[7]', 'VARCHAR(MAX)') AS Currentdbname7, A.value ('(/event/data/value/deadlock/process-list/process/@isolationlevel)[7]', 'VARCHAR(MAX)') AS isolationlevel7, A.value ('(/event/data/value/deadlock/process-list/process[7]/executionStack/frame/@sqlhandle)[1]', 'VARCHAR(MAX)') AS sql_handle7, sf7.text as full_text7, qp7.query_plan as query_plan7, A AS xml_report --,SUBSTRING(CAST(A as varchar(max)), CHARINDEX('<inputbuf>', CAST(A as varchar(max))), CHARINDEX('</inputbuf>',CAST(A as varchar(max))) - CHARINDEX('<inputbuf>', CAST(A as varchar(max))) + Len('</inputbuf>')) as VictimQuery, --SUBSTRING(CAST(A as varchar(max)), CHARINDEX('<inputbuf>',CAST(A as varchar(max)),CHARINDEX('<inputbuf>',CAST(A as varchar(max)))+10), CHARINDEX('</inputbuf>',CAST(A as varchar(max)),CHARINDEX('</inputbuf>',CAST(A as varchar(max)))+10) - CHARINDEX('<inputbuf>',CAST(A as varchar(max)),CHARINDEX('<inputbuf>',CAST(A as varchar(max)))+1) + Len('</inputbuf>')) as TheOtherQuery From XEvents x left join sys.databases d on x.A.value ('(/event/data/value/deadlock/resource-list/objectlock/@dbid)[1]', 'VARCHAR(MAX)') =d.database_id --left join sys.dm_exec_query_stats qs1 on qs1.sql_handle = CONVERT(VARBINARY(MAX), x.A.value ('(/event/data/value/deadlock/process-list/process/executionStack/frame/@sqlhandle)[1]', 'varchar(max)'), 1) outer apply sys.dm_exec_sql_text (CONVERT(VARBINARY(MAX), x.A.value ('(/event/data/value/deadlock/process-list/process/executionStack/frame/@sqlhandle)[1]', 'varchar(max)'), 1)) as sf1 outer apply sys.dm_exec_query_plan((select top 1 plan_handle from sys.dm_exec_query_stats where sql_handle = CONVERT(VARBINARY(MAX), x.A.value ('(/event/data/value/deadlock/process-list/process/executionStack/frame/@sqlhandle)[1]', 'varchar(max)'), 1)order by last_execution_time desc))as qp1 outer apply sys.dm_exec_sql_text (CONVERT(VARBINARY(MAX), x.A.value ('(/event/data/value/deadlock/process-list/process[2]/executionStack/frame/@sqlhandle)[1]', 'varchar(max)'), 1)) as sf2 outer apply sys.dm_exec_query_plan((select top 1 plan_handle from sys.dm_exec_query_stats where sql_handle = CONVERT(VARBINARY(MAX), x.A.value ('(/event/data/value/deadlock/process-list/process[2]/executionStack/frame/@sqlhandle)[1]', 'varchar(max)'), 1)order by last_execution_time desc))as qp2 outer apply sys.dm_exec_sql_text (CONVERT(VARBINARY(MAX), x.A.value ('(/event/data/value/deadlock/process-list/process[3]/executionStack/frame/@sqlhandle)[1]', 'varchar(max)'), 1)) as sf3 outer apply sys.dm_exec_query_plan((select top 1 plan_handle from sys.dm_exec_query_stats where sql_handle = CONVERT(VARBINARY(MAX), x.A.value ('(/event/data/value/deadlock/process-list/process[3]/executionStack/frame/@sqlhandle)[1]', 'varchar(max)'), 1)order by last_execution_time desc))as qp3 outer apply sys.dm_exec_sql_text (CONVERT(VARBINARY(MAX), x.A.value ('(/event/data/value/deadlock/process-list/process[4]/executionStack/frame/@sqlhandle)[1]', 'varchar(max)'), 1)) as sf4 outer apply sys.dm_exec_query_plan((select top 1 plan_handle from sys.dm_exec_query_stats where sql_handle = CONVERT(VARBINARY(MAX), x.A.value ('(/event/data/value/deadlock/process-list/process[4]/executionStack/frame/@sqlhandle)[1]', 'varchar(max)'), 1)order by last_execution_time desc))as qp4 outer apply sys.dm_exec_sql_text (CONVERT(VARBINARY(MAX), x.A.value ('(/event/data/value/deadlock/process-list/process[5]/executionStack/frame/@sqlhandle)[1]', 'varchar(max)'), 1)) as sf5 outer apply sys.dm_exec_query_plan((select top 1 plan_handle from sys.dm_exec_query_stats where sql_handle = CONVERT(VARBINARY(MAX), x.A.value ('(/event/data/value/deadlock/process-list/process[5]/executionStack/frame/@sqlhandle)[1]', 'varchar(max)'), 1)order by last_execution_time desc))as qp5 outer apply sys.dm_exec_sql_text (CONVERT(VARBINARY(MAX), x.A.value ('(/event/data/value/deadlock/process-list/process[6]/executionStack/frame/@sqlhandle)[1]', 'varchar(max)'), 1)) as sf6 outer apply sys.dm_exec_query_plan((select top 1 plan_handle from sys.dm_exec_query_stats where sql_handle = CONVERT(VARBINARY(MAX), x.A.value ('(/event/data/value/deadlock/process-list/process[6]/executionStack/frame/@sqlhandle)[1]', 'varchar(max)'), 1)order by last_execution_time desc))as qp6 outer apply sys.dm_exec_sql_text (CONVERT(VARBINARY(MAX), x.A.value ('(/event/data/value/deadlock/process-list/process[7]/executionStack/frame/@sqlhandle)[1]', 'varchar(max)'), 1)) as sf7 outer apply sys.dm_exec_query_plan((select top 1 plan_handle from sys.dm_exec_query_stats where sql_handle = CONVERT(VARBINARY(MAX), x.A.value ('(/event/data/value/deadlock/process-list/process[7]/executionStack/frame/@sqlhandle)[1]', 'varchar(max)'), 1)order by last_execution_time desc))as qp7 where 1=1 and DATEADD(HOUR,DATEDIFF(hour, SYSUTCDATETIME(),SYSDATETIME() ),A.value ('(/event/@timestamp)[1]', 'DATETIME')) between '2018-01-26 10:26:00.000' and '2018-12-26 10:28:00.000' --and d.name ='DB_1'
Το αποτέλεσμα
Το αποτέλεσμα του query μας έφερε για αυτό το χρονικό διάστημα στη βάση που του ζητήσαμε τα queries που ήτανε deadlocked, την λίστα με τα query μαζί με το process id, το locking mode, το isolation level που έτρεχε το καθένα και τέλος ποια ήταν τα Victim δηλαδή ποια έγιναν kill για να συνεχίσουν τα υπόλοιπα.
