數據庫阻塞:第一個連接占有資源沒有釋放,而第二個連接需要獲取這個資源。如果第一個連接沒有提交或者回滾,第二個連接會一直等待下去,直到第一個連接釋放該資源為止。對於阻塞,數據庫無法處理,所以對數據庫操作要及時地提交或者回滾。
阻塞示例:
第一個鏈接執行:
--創建表 CREATE TABLE [dbo].[A_Table]( [ID] [int] IDENTITY(1,1) NOT NULL, [NAME] [Nvarchar](50) NOT NULL , [CreateTime] [datetime] NOT NULL) --插入數據 INSERT INTO [dbo].[A_Table] ([NAME],[CreateTime]) VALUES ('joye1',GETDATE()) INSERT INTO [dbo].[A_Table] ([NAME],[CreateTime]) VALUES ('joye2',GETDATE()) INSERT INTO [dbo].[A_Table] ([NAME],[CreateTime]) VALUES ('joye3',GETDATE()) select * from [dbo].[A_Table] begin tran update [dbo].[A_Table] set [NAME]='joye' where ID=2 --commit tran第一個連接,不提交或者回滾:
第二個鏈接執行:
1
|
update
[dbo].[A_Table]
set
[
NAME
]=
'joye1'
where
ID=2
|
第二個一直在執行中,因為第一個連接占有A_Table表沒有釋放資源,而第二個連接一直在等待第一個連接釋放該資源。默認情況下除非設置了LOCK_TIMEOUT,否則事務會一直等待下去。
避免阻塞的最好方式就是及時的提交事務和回滾事務。
查詢阻塞的sql語句:
--sql查詢阻塞的sql語句 SELECT SPID=p.spid, DBName = convert(CHAR(20),d.name), ProgramName = program_name, LoginName = convert(CHAR(20),l.name), HostName = convert(CHAR(20),hostname), Status = p.status, BlockedBy = p.blocked, LoginTime = login_time, QUERY = CAST(TEXT AS VARCHAR(MAX)) FROM MASTER.dbo.sysprocesses p INNER JOIN MASTER.dbo.sysdatabases d ON p.dbid = d.dbid INNER JOIN MASTER.dbo.syslogins l ON p.sid = l.sid CROSS APPLY sys.dm_exec_sql_text(sql_handle) WHERE p.blocked = 0 AND EXISTS (SELECT 1 FROM MASTER..sysprocesses p1 WHERE p1.blocked = p.spid) --查詢引起阻塞和被阻塞的sql SELECT wt.blocking_session_id AS BlockingSessesionId ,sp.program_name AS Blocking_ProgramName ,COALESCE(sp.LOGINAME, sp.nt_username) AS Blocking_HostName ,ec1.client_net_address AS ClientIpAddress ,db.name AS DatabaseName ,wt.wait_type AS WaitType ,ec1.connect_time AS BlockingStartTime ,wt.WAIT_DURATION_MS/1000 AS WaitDuration ,ec1.session_id AS BlockedSessionId ,h1.TEXT AS BlockedSQLText ,h2.TEXT AS BlockingSQLText FROM sys.dm_tran_locks AS tl WITH(NOLOCK) INNER JOIN sys.databases AS db WITH(NOLOCK) ON db.database_id = tl.resource_database_id INNER JOIN sys.dm_os_waiting_tasks AS wt WITH(NOLOCK) ON tl.lock_owner_address = wt.resource_address INNER JOIN sys.dm_exec_connections ec1 WITH(NOLOCK) ON ec1.session_id = tl.request_session_id INNER JOIN sys.dm_exec_connections ec2 WITH(NOLOCK) ON ec2.session_id = wt.blocking_session_id LEFT OUTER JOIN master.dbo.sysprocesses AS sp WITH(NOLOCK) ON SP.spid = wt.blocking_session_id CROSS APPLY sys.dm_exec_sql_text(ec1.most_recent_sql_handle) AS h1 CROSS APPLY sys.dm_exec_sql_text(ec2.most_recent_sql_handle) AS h2
數據庫死鎖:第一個連接占有資源沒有釋放,准備獲取第二個連接所占用的資源,而第二個連接占有資源沒有釋放, 准備獲取第一個連接所占用的資源。這種互相占有對方需要獲取的資源的現象叫做死鎖。對於死鎖,數據庫處理方法:犧牲一個。
死鎖實例:
創建B表和C表
--創建B表 CREATE TABLE [dbo].[B_Table]( [ID] [int] IDENTITY(1,1) NOT NULL, [NAME] [Nvarchar](50) NOT NULL , [CreateTime] [datetime] NOT NULL) --插入數據 INSERT INTO [dbo].[B_Table] ([NAME],[CreateTime]) VALUES ('joye1',GETDATE()) INSERT INTO [dbo].[B_Table] ([NAME],[CreateTime]) VALUES ('joye2',GETDATE()) INSERT INTO [dbo].[B_Table] ([NAME],[CreateTime]) VALUES ('joye3',GETDATE()) select * from [dbo].[B_Table] begin tran update [dbo].[B_Table] set [NAME]='joye' where ID=2 waitfor delay '0:0:20' update [dbo].[C_Table] set [NAME]='joye' where ID=2 --commit tran
--創建C表 CREATE TABLE [dbo].[C_Table]( [ID] [int] IDENTITY(1,1) NOT NULL, [NAME] [Nvarchar](50) NOT NULL , [CreateTime] [datetime] NOT NULL) --插入數據 INSERT INTO [dbo].[C_Table] ([NAME],[CreateTime]) VALUES ('joye1',GETDATE()) INSERT INTO [dbo].[C_Table] ([NAME],[CreateTime]) VALUES ('joye2',GETDATE()) INSERT INTO [dbo].[C_Table] ([NAME],[CreateTime]) VALUES ('joye3',GETDATE()) select * from [dbo].[C_Table] begin tran update [dbo].[C_Table] set [NAME]='joye' where ID=2 waitfor delay '0:0:20' update [dbo].[B_Table] set [NAME]='joye' where ID=2 --commit tran
執行結果:
一個事務執行成功,如圖
下個事務被犧牲:
消息 1205,級別 13,狀態 45,第 26 行
事務(進程 ID 58)與另一個進程被死鎖在 鎖 資源上,並且已被選作死鎖犧牲品。請重新運行該事務。
因為第一個連接占有表B,想要獲取表C的資源,而第二個連接占有表C,想要獲取表B的資源,這種互相占有對方想要獲取的資源,滿足死鎖現象。最后第一個連接執行成功,而第二個連接事務被選作死鎖犧牲品。兩個或多個進程之間的相互等待。但是由於SQL Server有數據庫引擎死鎖檢測方案,至少5秒鍾會消除一個現有的死鎖。對性能的影響往往沒有阻塞嚴重。
避免死鎖:
盡管死鎖不能完全避免,但是可以把機會降到最低:
按同一順序訪問對象(如果所有並發事務按同一順序訪問對象,則發生死鎖的可能性會降低。)
避免事務中的用戶交互(避免編寫包含用戶交互的事務,因為沒有用戶干預的批處理的運行速度遠快於必須等待用戶響應時的查詢速度。
保持事務簡短並處於一個批處理中(運行時間越長,等待時間就越長,造成死鎖的機會就越高。)
使用較低的隔離級別(確定事務能否在低隔離級別上運行。盡可能使用較低的隔離級別。)
調整語句的執行計划,減少鎖的申請數目(可以從執行計划中找出哪些資源耗得比較多。此時鎖的數目也會相應增多)
查詢死鎖:
-- 查詢死鎖 select request_session_id spid,OBJECT_NAME(resource_associated_entity_id) tableName from sys.dm_tran_locks where resource_type='OBJECT' --查詢主機名,數據庫等信息 exec sp_who2 59 --殺死死鎖進程 kill 59
查詢如下:
也可使用SQL Server Profile 跟蹤阻塞或者死鎖的SQL