數據庫系統異常是DBA經常要面臨的情景,一名有一定從業經驗的DBA,都會有自己一套故障排查的方法和步驟,此文為為大家介紹一下通過系統
性能視圖(SQLServer05以上版本)來排查系統異常的基本方法,希望能對大家有所幫助。
這里分兩部分來介紹:
一. 從數據庫連接情況來判斷異常:
1. 首先我們來看一下目前數據庫系統所有請求情況:
--request info
select s.session_id, s.status,db_name(r.database_id) as database_name,
s.login_name,s.login_time, s.host_name,
c.client_net_address,c.client_tcp_port,s.program_name,
r.cpu_time, r.reads, r.writes,c.num_reads,c.num_writes,
s.client_interface_name,
s.last_request_start_time, s.last_request_end_time,
c.connect_time, c.net_transport, c.net_packet_size,
r.start_time, r.status, r.command,
r.blocking_session_id, r.wait_type,
r.wait_time, r.last_wait_type, r.wait_resource, r.open_transaction_count,
r.percent_complete,r.granted_query_memory
from Sys.dm_exec_requests r with(nolock)
right outer join Sys.dm_exec_sessions s with(nolock)
on r.session_id = s.session_id
right outer join Sys.dm_exec_connections c with(nolock)
on s.session_id = c.session_id
where s.session_id >50
order by s.session_id
某台生產機運行情況:

這個查詢將目前數據庫中的所有請求都顯示出來了,其中比較重要的有Status、Login_name、Host_Name,Client_Net_Address、Program_name
等,但是信息比較多,我們很難查看有什么異常,但是可以通過一圖中紅色圈的數字:441 初步判斷連接數是否超過了平時的標准(很多時候系統異常是連接
數過多造成的,而連接數過多又是因為其他原因影響的)。
2. 哪個用戶連接數最多:
--request info by user
select login_name,COUNT(0) user_count
from Sys.dm_exec_requests r with(nolock)
right outer join Sys.dm_exec_sessions s with(nolock)
on r.session_id = s.session_id
right outer join Sys.dm_exec_connections c with(nolock)
on s.session_id = c.session_id
where s.session_id >50
group by login_name
order by 2 desc
運行結果:

從圖中我們可以很方便的看出用戶連接數情況,如果我們的不同的功能是使用不同的的數據庫賬號的話,就能初步判斷是哪部分功能可能出現了異常。
3. 哪台機器發起到數據庫的連接數最多:
--request info by hostname
select s.host_name,c.client_net_address,COUNT(0) host_count
from Sys.dm_exec_requests r with(nolock)
right outer join Sys.dm_exec_sessions s with(nolock)
on r.session_id = s.session_id
right outer join Sys.dm_exec_connections c with(nolock)
on s.session_id = c.session_id
where s.session_id >50
group by host_name,client_net_address
order by 3 desc
運行結果:

這個查詢能夠一下就幫我們找出來哪些機器發起了對數據庫的鏈接,它們的鏈接數量是否有異常;這個其實對調查某些問題非常有用,我有一次就遇
到一個case:
用戶反映,過一兩個星期,系統就會出現一次異常,出問題時數據庫連接數量很高,大量的訪問被數據庫拒絕,過半個小時左右,系統又自動恢復了,但是
在數據庫里面查看,並沒有發現有異常的進程和錯誤的信息,問題一時很棘手,很難定位,系統不穩定領導不滿,DBA頂着壓力一時不知道如何是好;后面
轉換方向,通過調查問題發生時,為什么會產生這么多連接,這些連接是那些機器發過來的,這些連接發過來正常嗎,是數據庫不砍業務的重負,還是業務
在某個時間段內會出現暴漲等一系列原因,最終找出是一台Web因為開發人員代碼寫的有問題,內存出現內存泄露,導致大量的連接不能釋放,出問題是,
發出的數據庫連接數比平時高3-4倍,最終影響到了數據庫,問題壓根和數據庫沒關系(從這個事實看出,DBA真是的炮灰角色,不是自己的問題,也得頂
着壓力調查出原因呀);如果在類似問題發生時,我們能通過這個查詢及早知道問題是出在某台Web機器上,那就不用費盡心力來調查數據庫了。
4. 這些連接在訪問哪個庫:
--request info by databases
select db_name(r.database_id) as database_name,COUNT(0) host_count
from Sys.dm_exec_requests r with(nolock)
right outer join Sys.dm_exec_sessions s with(nolock)
on r.session_id = s.session_id
right outer join Sys.dm_exec_connections c with(nolock)
on s.session_id = c.session_id
where s.session_id >50
group by r.database_id
order by 2 desc
結果(為NULL的估計是沒辦法定位庫):

5. 進程狀態:
--request info by status
select s.status,COUNT(0) host_count
from Sys.dm_exec_requests r with(nolock)
right outer join Sys.dm_exec_sessions s with(nolock)
on r.session_id = s.session_id
right outer join Sys.dm_exec_connections c with(nolock)
on s.session_id = c.session_id
where s.session_id >50
group by s.status
order by 2 desc
結果(running數比較多,表面數據庫壓力比較大):

二. 從阻塞情況來判斷異常(這部分內容不再一一貼圖,直接上腳本):
1. 查看數據庫阻塞情況:
----------------------------------------Blocked Info----------------------------------
--記錄當前阻塞信息
select t1.resource_type as [lock type] ,db_name(resource_database_id) as [database]
,t1.resource_associated_entity_id as [blk object]
,t1.request_mode as [lock req] -- lock requested
,t1.request_session_id as [waiter sid] -- spid of waiter
,t2.wait_duration_ms as [wait time]
,(select text from sys.dm_exec_requests as r with(nolock) --- get sql for waiter
cross apply sys.dm_exec_sql_text(r.sql_handle)
where r.session_id = t1.request_session_id) as waiter_batch
,(select substring(qt.text,r.statement_start_offset/2,
(case when r.statement_end_offset = -1 then len(convert(nvarchar(max), qt.text)) * 2
else r.statement_end_offset end - r.statement_start_offset)/2+1)
from sys.dm_exec_requests as r with(nolock)
cross apply sys.dm_exec_sql_text(r.sql_handle) as qt
where r.session_id = t1.request_session_id) as waiter_stmt --- statement executing now
,t2.blocking_session_id as [blocker sid] --- spid of blocker
,(select text from sys.sysprocesses as p with(nolock) --- get sql for blocker
cross apply sys.dm_exec_sql_text(p.sql_handle)
where p.spid = t2.blocking_session_id) as blocker_stmt,getdate() time
from sys.dm_tran_locks as t1 with(nolock) , sys.dm_os_waiting_tasks as t2 with(nolock)
where t1.lock_owner_address = t2.resource_address
2. 查看阻塞其他進程的進程(阻塞源頭):
--阻塞其他session的session
select t2.blocking_session_id,COUNT(0) counts
from sys.dm_tran_locks as t1 with(nolock) , sys.dm_os_waiting_tasks as t2 with(nolock)
where t1.lock_owner_address = t2.resource_address
group by blocking_session_id
order by 2
3. 被阻塞時間最長的進程:
--被阻塞時間最長的session
select top 10 t1.resource_type as [lock type] ,db_name(resource_database_id) as [database]
,t1.resource_associated_entity_id as [blk object]
,t1.request_mode as [lock req] -- lock requested
,t1.request_session_id as [waiter sid] -- spid of waiter
,t2.wait_duration_ms as [wait time]
,(select text from sys.dm_exec_requests as r with(nolock) --- get sql for waiter
cross apply sys.dm_exec_sql_text(r.sql_handle)
where r.session_id = t1.request_session_id) as waiter_batch
,(select substring(qt.text,r.statement_start_offset/2,
(case when r.statement_end_offset = -1 then len(convert(nvarchar(max), qt.text)) * 2
else r.statement_end_offset end - r.statement_start_offset)/2+1)
from sys.dm_exec_requests as r with(nolock)
cross apply sys.dm_exec_sql_text(r.sql_handle) as qt
where r.session_id = t1.request_session_id) as waiter_stmt --- statement executing now
,t2.blocking_session_id as [blocker sid] --- spid of blocker
,(select text from sys.sysprocesses as p with(nolock) --- get sql for blocker
cross apply sys.dm_exec_sql_text(p.sql_handle)
where p.spid = t2.blocking_session_id) as blocker_stmt,getdate() time
from sys.dm_tran_locks as t1 with(nolock) , sys.dm_os_waiting_tasks as t2 with(nolock)
where t1.lock_owner_address = t2.resource_address
order by t2.wait_duration_ms desc
此文大致總結了通過DMV調查數據庫異常的基本方法和步驟,如果大家在調查問題時能夠靈活運用,相信對數據庫異常情況的定位和解決能夠更快更有效。
