記一次數據庫調優過程(IIS發過來SQLSERVER 的FETCH API_CURSOR語句是神馬?)

記一次數據庫調優過程(IIS發過來SQLSERVER 的FETCH API_CURSOR語句是神馬?)

前幾天幫客戶優化一個數據庫,那個數據庫的大小是6G程序員

這麼小的數據庫按道理不會有太大的性能問題的,可是客戶反應說CPU佔用很高,常常達到80%~90%sql

我檢查了任務管理器,確實是SQLSERVER佔的CPU數據庫

而服務器的內存是16G內存,只佔用了7G+api

客戶的環境:服務器

Windows2008R2session

SQLSERVER2005 SP3 64位 企業版ide

服務器內存:16Gsqlserver

CPU:8核性能

RDS:阿里雲主機fetch

IIS7.5

網站使用ASP技術

 


着手查找緣由

因而就着手檢查佔用CPU高的緣由,檢查了好久,發現有一些SQL語句佔用CPU很高,而執行的SQL語句以下:

 

 這些是什麼語句呢?在msdn上面找不到任何資料,使用下面的SQL語句查看,在[program_name]字段能夠看到是IIS發過來的

SELECT * FROM sys.[sysprocesses] WHERE SPID>=50

難道是IIS的bug?而後我又繼續在茫茫網海里查找資料,最後終於在paul的博客裏找到緣由

文章地址:Hunting down the origins of FETCH API_CURSOR and sp_cursorfetch


文章大意

我在調優數據庫的時候,使用sqlserver profiler捕獲RPC:Completed 事件,能夠看到不少相似下面的語句

exec sp_cursorfetch 180150003,32,1,1
exec sp_cursorfetch 180150003,32,1,1
exec sp_cursorfetch 180150003,32,1,1
exec sp_cursorfetch 180150003,32,1,1

 

你看到這些語句是從session_id爲53的session那裏發過來的

因而用下面語句看一下session_id爲53執行的到底是什麼語句

DBCC INPUTBUFFER (53)

 

而返回的結果是

 FETCH API_CURSOR0000000000000004

 

您很快意識到這跟服務器遊標有必定的關係

 

若是你使用sys.dm_exec_requests 視圖或者sys.dm_exec_connections視圖來查看session_id53執行了什麼語句

和執行的狀態

SELECT t.text
FROM sys.dm_exec_connections c
CROSS APPLY sys.dm_exec_sql_text (c.most_recent_sql_handle) t
WHERE session_id = 53

可是返回的結果依然是

FETCH API_CURSOR0000000000000004

 

那麼還有沒有其餘的視圖來幫助咱們呢?咱們可使用sys.dm_exec_cursors視圖,將spid代入進去

SELECT c.session_id, c.properties, c.creation_time, c.is_open, t.text
FROM sys.dm_exec_cursors (53) c
CROSS APPLY sys.dm_exec_sql_text (c.sql_handle) t

從結果來看,咱們知道語句使用了遊標,而且知道遊標的屬性(scroll locks)和遊標建立時間

而且咱們看到執行的SQL語句不像是FETCH API_CURSOR或者sp_cursorfetch,而是

SELECT * FROM dbo.FactResellerSales.

本人的處理過程

一、先使用下面的SQL語句找出當前實例下有使用到遊標的語句

-- =============================================
-- Author:      <樺仔>
-- Blog:        <http://www.cnblogs.com/lyhabc/>
-- Create date: <2014/6/3>
-- Description: <獲取當前實例下全部的遊標語句>
-- =============================================
DECLARE @spid NVARCHAR(100)
DECLARE @SQL NVARCHAR(MAX)

DECLARE CurSPID CURSOR
FOR
    SELECT  [spid]
    FROM    sys.[sysprocesses]
    WHERE   [spid] >= 50

OPEN CurSPID
FETCH NEXT FROM CurSPID INTO @spid

WHILE @@FETCH_STATUS = 0
    BEGIN  
        SET @SQL = N'
SELECT  cursors.session_id ,
        cursors.properties ,
        cursors.creation_time ,
        cursors.is_open ,
        text.text
FROM    sys.dm_exec_cursors (' + @spid + ') cursors
        CROSS APPLY sys.dm_exec_sql_text(cursors.sql_handle) text'
        EXEC(@SQL) 

        FETCH NEXT FROM CurSPID INTO @spid
    END
CLOSE CurSPID
DEALLOCATE CurSPID

 

爲什麼上面的腳本要使用遊標,由於當時我根據paul的腳原本執行的時候,在活動監視器裏能看到使用遊標的SQL語句,

可是在SSMS裏查詢的時候,怎麼也查詢不出來,因此才用遊標,將使用到遊標的語句一網打盡,這裏輸出的結果要忽略自己這個腳本使用到的遊標!!

 

二、根據輸出的結果,發現有幾個地方使用了遊標,下面只是部分截圖

 

三、把結果拷貝出來,能夠發現也是執行的是SELECT 語句

 

四、由於是ASP程序,沒有用到存儲過程,因而搜索項目文件,看一下哪一個文件有相似的代碼

五、找到結果

 

ASP的語法跟VB是很像的,本人以爲很是羞澀

能夠看到server對象建立了一個recordset對象,而後從recordset對象裏逐條記錄取出來,再作處理,能夠看到後續還有

select case....case...case....

就是對取出來的記錄再作處理

 

由於ASP是腳本語言,由IIS來執行,因此在SQLSERVER這邊能夠看到下面語句的program_name字段是IIS

SELECT * FROM sys.[sysprocesses] WHERE SPID>=50

 

六、驗證一下是不是遊標的緣由致使CPU高,使用下面的腳本

SELECT * FROM sys.[dm_os_performance_counters] 
WHERE [counter_name]='CPU usage %'   
AND [object_name]='SQLServer:Resource Pool Stats'     
AND [instance_name]='default'                                                


SELECT * FROM sys.[dm_os_performance_counters] 
WHERE [counter_name]='Active cursors'   
AND [object_name]='SQLServer:Cursor Manager by Type'     
AND [instance_name]='_Total'                                                


--建表
USE [msdb]
GO
CREATE TABLE ActiveCursors
(cntr_value BIGINT,cntr_time DATETIME PRIMARY KEY)
GO
CREATE TABLE CPUUsage
(cntr_value BIGINT,cntr_time DATETIME PRIMARY KEY)
GO


--建做業
DECLARE @DBName NVARCHAR(MAX)
DECLARE @job_name sysname

SET @DBName='xxx'  --★Do

SET @job_name='Monitor_CPUUsage_' + @DBName
EXEC msdb.dbo.sp_add_job @job_name=@job_name, 
@enabled=1, 
@notify_level_eventlog=0, 
@notify_level_email=0, 
@notify_level_netsend=0, 
@notify_level_page=0, 
@delete_level=0, 
@description=N'監控CPU使用率', 
@category_name=N'Database Maintenance', 
@owner_login_name=N'sa' 


--添加監控步驟
DECLARE @job_name SYSNAME
DECLARE @SQL NVARCHAR(MAX)
DECLARE @DBName NVARCHAR(MAX)

SET @DBName='xxx'  --★Do
SET @job_name='Monitor_CPUUsage_' + @DBName  --★Do

BEGIN 
    SET @SQL = N'
USE [msdb]
GO
INSERT INTO CPUUsage(cntr_value,cntr_time)  
SELECT cntr_value,GETDATE() FROM sys.[dm_os_performance_counters] 
WHERE [counter_name]=''CPU usage %''   
AND [object_name]=''SQLServer:Resource Pool Stats''   
AND [instance_name]=''default''
'
    EXEC msdb.dbo.sp_add_jobstep @job_name = @job_name,
        @step_name = N'Monitor', @step_id = 1, @cmdexec_success_code = 0,
        @on_success_action = 3, @on_success_step_id = 0, @on_fail_action = 2,
        @on_fail_step_id = 0, @retry_attempts = 0, @retry_interval = 0,
        @os_run_priority = 0, @subsystem = N'TSQL', @command = @SQL,
        @database_name = @DBNAME, @flags = 0

END

  


--建立Monitor做業的調度計劃
DECLARE @job_name SYSNAME
DECLARE @SQL NVARCHAR(MAX)
DECLARE @DBName NVARCHAR(MAX)

SET @DBName='xxx'  --★Do
SET @job_name='Monitor_CPUUsage_' + @DBName  --★Do

--修改做業的執行時間
EXEC  msdb.dbo.sp_add_jobschedule  @job_name = @job_name, @name=N'Plan', 
        @enabled=1, 
        @freq_type=4, 
        @freq_interval=1, 
        @freq_subday_type=2, 
        @freq_subday_interval=30, 
        @freq_relative_interval=0, 
        @freq_recurrence_factor=0, 
        @active_start_date=20140105, 
        @active_end_date=99991231, 
        @active_start_time=2000, 
        @active_end_time=235959

EXEC  msdb.dbo.sp_add_jobserver  @job_name = @job_name, @server_name = N'(local)'





------------------------------------------------------------------------------
--建做業
DECLARE @DBName NVARCHAR(MAX)
DECLARE @job_name sysname

SET @DBName='xxx'  --★Do

SET @job_name='Monitor_ActiveCursors_' + @DBName
EXEC msdb.dbo.sp_add_job @job_name=@job_name, 
@enabled=1, 
@notify_level_eventlog=0, 
@notify_level_email=0, 
@notify_level_netsend=0, 
@notify_level_page=0, 
@delete_level=0, 
@description=N'監控遊標使用', 
@category_name=N'Database Maintenance', 
@owner_login_name=N'sa' 


--添加監控步驟
DECLARE @job_name SYSNAME
DECLARE @SQL NVARCHAR(MAX)
DECLARE @DBName NVARCHAR(MAX)

SET @DBName='xxxx'  --★Do
SET @job_name='Monitor_ActiveCursors_' + @DBName  --★Do

BEGIN 
    SET @SQL = N'
USE [msdb]
GO
INSERT INTO ActiveCursors(cntr_value,cntr_time)  
SELECT cntr_value,GETDATE()  FROM sys.[dm_os_performance_counters] 
WHERE [counter_name]=''Active cursors''   
AND [object_name]=''SQLServer:Cursor Manager by Type''  
AND [instance_name]=''_Total''
'
    EXEC msdb.dbo.sp_add_jobstep @job_name = @job_name,
        @step_name = N'Monitor', @step_id = 1, @cmdexec_success_code = 0,
        @on_success_action = 3, @on_success_step_id = 0, @on_fail_action = 2,
        @on_fail_step_id = 0, @retry_attempts = 0, @retry_interval = 0,
        @os_run_priority = 0, @subsystem = N'TSQL', @command = @SQL,
        @database_name = @DBNAME, @flags = 0

END

  


--建立Monitor做業的調度計劃
DECLARE @job_name SYSNAME
DECLARE @SQL NVARCHAR(MAX)
DECLARE @DBName NVARCHAR(MAX)

SET @DBName='xxxx'  --★Do
SET @job_name='Monitor_ActiveCursors_' + @DBName  --★Do

--修改做業的執行時間
EXEC  msdb.dbo.sp_add_jobschedule  @job_name = @job_name, @name=N'Plan', 
        @enabled=1, 
        @freq_type=4, 
        @freq_interval=1, 
        @freq_subday_type=2, 
        @freq_subday_interval=30, 
        @freq_relative_interval=0, 
        @freq_recurrence_factor=0, 
        @active_start_date=20140105, 
        @active_end_date=99991231, 
        @active_start_time=2000, 
        @active_end_time=235959

EXEC  msdb.dbo.sp_add_jobserver  @job_name = @job_name, @server_name = N'(local)'
View Code

上面視圖裏的[object_name]字段和 [instance_name]字段跟你的環境會不同,因此你們要按照本身的環境來修改

若是是SQLSERVER2005是沒有CPU usage %這個counter的,我使用了下面的SQL語句

SELECT SUM([cpu]) FROM sys.[sysprocesses] WHERE SPID>=50

 

七、畫折線圖

監控了一天的時間,根據結果使用EXCEL畫出折線圖

 

 

凌晨那段曲線是由於數據庫有作清除數據的操做,因此會比較高

遊標跟CPU圖雖說不能徹底吻合,可是基本能吻合

 

解決方法

一、修改代碼

二、升級到SQL2008,而後使用資源調控器把CPU壓下去

 

最終仍是找人修改代碼


總結

有時候對一些老舊的程序,例如ASP,可能老一代程序員還會,如今的程序員基本都使用ASP.NET

因此若是可能,仍是跟上技術的腳步,否則出問題了,沒有人維護就麻煩了

 

若有不對的地方,歡迎你們拍磚o(∩_∩)o 

相關文章
相關標籤/搜索