1. SQL死锁概念
在多个并发执行的事务中,由于资源竞争而导致它们都等待对方释放资源,从而无法继续进行下去,这种现象就叫做死锁。
SQL死锁是指在SQL Server数据库系统中,由于多个Session之间发生竞争、等待,而导致所有Session都陷入等待状态,无法继续执行操作,从而导致整个数据库系统处于一种死锁状态。
2. SQL死锁检测的方法
2.1 查看死锁日志
SQL Server 默认自带死锁日志,可以通过查看死锁日志来识别在哪些对象上有死锁发生。可使用下面的命令查看死锁日志:
SELECT * FROM sys.fn_get_audit_file('', default, default)
WHERE [action_id] = 'LCK_DEADLOCK';
上述 SQL 命令会将死锁日志文件内容以表格形式输出。
可使用 SQL Server Profiler 工具定位死锁日志,并分析初步定位死锁问题。
2.2 使用系统视图监视死锁
如果没有启用死锁日志或死锁日志的打印间隔较长,也可以通过监视内置的系统视图来查看当前正在发生死锁的事务。
SELECT
tran.session_id AS [Transaction ID],
resc.resource_type AS [锁类型],
resc.resource_associated_entity_id AS [锁的资源 ID],
resc.request_mode AS [请求模式],
resc.request_session_id AS [请求会话 ID],
tran.request_type AS [请求类型],
tran.request_mode AS [请求模式],
tran.request_status AS [请求状态],
tran.request_session_id AS [请求会话 ID],
tran.transaction_id AS [事务 ID],
tran.transaction_sequence_num AS [事务序列号],
tran.pool_id AS [连接池 ID],
tran.is_member AS [事务成员],
tran.is_local AS [本地事务],
tran.is_suspended AS [我们是否被挂起]
FROM sys.dm_tran_locks AS l
JOIN sys.dm_tran_database_transactions AS tran
ON l.request_owner_id = tran.transaction_id
JOIN sys.dm_tran_active_transactions AS acttran
ON tran.transaction_id = acttran.transaction_id
JOIN sys.dm_tran_session_transactions AS sesstran
ON acttran.transaction_id = sesstran.transaction_id
JOIN sys.dm_exec_sessions AS sess
ON sess.session_id = sesstran.session_id
JOIN sys.dm_os_waiting_tasks AS wait
ON sess.session_id = wait.session_id
JOIN sys.dm_os_waiting_tasks AS waitresc
ON wait.waiting_task_address = waitresc.blocking_task_address
JOIN sys.dm_os_latch_stats AS latch
ON waitresc.resource_address = latch.address
JOIN sys.dm_tran_locks AS resc
ON latch.latch_address = resc.resource_associated_entity_id
WHERE tran.transaction_state <> 2;
使用该命令,可以查看syst.dm_tran_locks中的记录。
2.3 使用SQL Profiler监视死锁
通过 SQL Server Profiler 工具,可以监视发生死锁的事务,并查看哪些 SQL 语句可能导致死锁。
2.4 查看阻塞情况
死锁与阻塞不同,没有被杀死的进程会保持死锁状态,不会继续执行,而阻塞进程仍在执行,但正在等待某个资源。可以通过下面的命令查看阻塞情况:
SELECT
blocking_session_id AS SPID,
wait_duration_ms AS [WaitTime(ms)],
wait_type,
wait_resource,
blocked.session_id as BlockedSPID,
ST.text AS [Blocked Query],
RTRIM(DB.name) + '.' + RTRIM(OBJECT_SCHEMA_NAME(ST.objectid, dbid)) + '.' + RTRIM(OBJECT_NAME(ST.objectid, dbid)) AS [Blocked Object]
FROM sys.dm_os_waiting_tasks AS wt
JOIN sys.dm_exec_sessions AS blocking ON wt.session_id = blocking.session_id
JOIN sys.dm_exec_requests AS blocking_req ON blocking.session_id = blocking_req.session_id
CROSS APPLY sys.dm_exec_sql_text(blocking_req.sql_handle) AS ST
JOIN sys.dm_exec_sessions AS blocked ON wt.blocking_session_id = blocked.session_id
JOIN sys.databases AS DB on ST.dbid = DB.database_id
3. 总结
SQL死锁问题需要及时诊断和解决。我们可以通过死锁日志、系统视图、SQL Server Profiler和查询阻塞情况等多种方式来定位问题。
如果经过多次尝试依然无法解决死锁问题,建议加强数据库的设计,减少对同一资源的竞争。