记一次数据库调优过程(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 

相关文章
相关标签/搜索