记一次数据库调优过程(IIS发过来SQLSERVER 的FETCH API_CURSOR语句是神马?)

记一次数据库调优过程(IIS发过来SQLSERVER 的FETCH
API_CURSOR语句是神马?)


前几天帮客户优化一个数据库,那个数据库的大小是6G

这麽小的数据库按道理不会有太大的性能问题的,但是客户反应说CPU占用很高,经常达到80%~90%

我检查了任务管理器,确实是SQLSERVER占的CPU

而服务器的内存是16G内存,只占用了7G+

客户的环境:

Windows2008R2

SQLSERVER2005 SP3 64位 企业版

服务器内存:16G

CPU:8核

RDS:阿里云主机

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.


本人的处理过程

1、先使用下面的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里查询的时候,怎麽也查询不出来,所以才用游标,将使用到游标的语句一网打尽,这里输出的结果要忽略本身这个脚本使用到的游标!!

2、根据输出的结果,发现有几个地方使用了游标,下面只是部分截图

3、把结果拷贝出来,可以发现也是执行的是SELECT 语句

4、因为是ASP程序,没有用到存储过程,于是搜索项目文件,看一下哪个文件有类似的代码

5、找到结果

ASP的语法跟VB是很像的,本人觉得非常羞涩

可以看到server对象创建了一个recordset对象,然后从recordset对象里逐条记录取出来,再做处理,可以看到后续还有

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

就是对取出来的记录再做处理

因为ASP是脚本语言,由IIS来执行,所以在SQLSERVER这边可以看到下面语句的program_name字段是IIS


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

6、验证一下是否是游标的原因导致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)‘

上面视图里的[object_name]字段和 [instance_name]字段跟你的环境会不一样,所以大家要按照自己的环境来修改

如果是SQLSERVER2005是没有CPU usage %这个counter的,我使用了下面的SQL语句


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

7、画折线图

监控了一天的时间,根据结果使用EXCEL画出折线图

凌晨那段曲线是因为数据库有做清除数据的操作,所以会比较高

游标跟CPU图虽然说不能完全吻合,但是基本能吻合

解决方法

1、修改代码

2、升级到SQL2008,然后使用资源调控器把CPU压下去

最终还是找人修改代码


总结

有时候对一些老旧的程序,例如ASP,可能老一代程序员还会,现在的程序员基本都使用ASP.NET

所以如果可能,还是跟上技术的脚步,不然出问题了,没有人维护就麻烦了

如有不对的地方,欢迎大家拍砖o(∩_∩)o 

记一次数据库调优过程(IIS发过来SQLSERVER 的FETCH API_CURSOR语句是神马?),布布扣,bubuko.com

时间: 2024-10-20 23:55:26

记一次数据库调优过程(IIS发过来SQLSERVER 的FETCH API_CURSOR语句是神马?)的相关文章

数据库调优过程(三):提高数据库写入性能方案调查

客户给出建议方案: 一直听客户这边说mycat这个东东,一头雾水,这是什么东东,只听说过mysql不知道mycat,瞬间被鄙视N次方次. 客户提到数据库分片.表复制能提高数据库性能,但可惜对这些技术好像不知道,只知道表分区,不知道分片,知道表批量复制,可以被订阅推送等,及主从复制,不知道这里说的表复制到时所谓什么技术. 客户提到MongoDB,PostgreSQL比较强悍,基本上可以实现集群型结构,表分片相关复制相关技术,但到底在实际应用中怎么是个怎么运行的. 带着上边的问题,我认真的查阅了网上

数据库调优过程(四):提高数据库写入修改方案

目前先后做了以下调整: 1.单表多个线程写入的时候,总会因为写入log或单文件写入速度的限制,导致IO提到不了成为入库的瓶颈:为了提到入库IO速度,我们采取分表策略,一次性拆分到一个ENB为单元的物理表: 2.修改批量入库方案:从监控IO来看,IO的写入速度确实有提高,但是同时写入多个表过程是单线程批量写入单个表,后来修改为并行写入多个表.但是依然存在内存居高不下的情况,这样的一个enb往往会造成内存居高不下,以至于内存资源成为瓶颈: 3.为了解决内存成为瓶颈,在内存处理时,每当表记录到达50w

MySQL面试必考知识点:揭秘亿级高并发数据库调优与最佳实践法则

做业务,要懂基本的SQL语句: 做性能优化,要懂索引,懂引擎: 做分库分表,要懂主从,懂读写分离... 数据库的使用,是开发人员的基本功,对它掌握越清晰越深入,你能做的事情就越多. 今天我们用10分钟,重点梳理一遍以下几方面: 数据库知识点汇总: 数据库事务特性和隔离级别: 详解关系型数据库.索引与锁机制: 数据库调优与最佳实践: 面试考察点及加分项. 知识点汇总 一.数据库的不同类型 1.常用的关系型数据库 Oracle:功能强大,主要缺点就是贵 MySQL:互联网行业中最流行的数据库,这不仅

[转]10分钟梳理MySQL知识点:揭秘亿级高并发数据库调优与最佳实践法则

转:https://mp.weixin.qq.com/s/RYIiHAHHStIMftQT6lQSgA 做业务,要懂基本的SQL语句: 做性能优化,要懂索引,懂引擎: 做分库分表,要懂主从,懂读写分离... 数据库的使用,是开发人员的基本功,对它掌握越清晰越深入,你能做的事情就越多. 今天我们用10分钟,重点梳理一遍以下几方面: 数据库知识点汇总: 数据库事务特性和隔离级别: 详解关系型数据库.索引与锁机制: 数据库调优与最佳实践: 面试考察点及加分项. 一.数据库的不同类型 1.常用的关系型数

(转)从“如何设计用户超过1亿的应用”说起—数据库调优实战

摘要:如果企业计划构建高性能的SaaS应用,仅凭云服务基础设施是不够的.如何基于云服务平台设计并实施符合自身业务特点的系统架构,是决定产品性能的关键.本文将讲述我们如何利用云服务,解决海量用户的数据库使用问题. 杭州湖畔网络技术有限公司是一家专业提供SaaS化电商ERP服务的创业公司,主要用户群体为经营淘宝.天猫.京东等主流电商平台.自建商城.线下渠道的商家及中小企业.作为SaaS服务提供商,服务数万乃至数十万级用户是业务架构初期就必须考虑的问题.庞大的用户群以及海量的用户数据意味着基础设施的构

数据库调优的方法有那些

1.引言        数据库调优可以使数据库应用运行得更快,它需要综合考虑各种复杂的因素.将数据均 匀分布在磁盘上可以提高I/O 利用率,提高数据的读写性能:适当程度的非规范化可以改善 系统查询性能:建立索引和编写高效的SQL 语句能有效避免低性能操作:通过锁的调优解 决并发控制方面的性能问题.     数据库调优技术可以在不同的数据库系统中使用,它不必纠缠于复杂的公式和规则,然 而它需要对程序的应用.数据库管理系统.查询处理.并发控制.操作系统以及硬件有广泛 而深刻的理解. 2.计算机硬件调

MySQL数据库调优经验

?RDS for MySQL 由亚洲唯一WebScaleSQL团队维护内核源码,结合阿里巴巴多年MySQL数据库调优经验,从数据库源码层及数据库参数进行了性能优化,在相近规格配置下,RDS for MySQL性能值能达到自建数据库性能的3倍以上. RDS for MySQL针对通用的场景,在内核做了一系列的优化: 1. 改进了InnoDB redo组提交功能,多线程并发写入的情况下能有10%以上的速度提升. 2. 优化锁,对一些会引起串行化的大锁进行了拆分,能够有效避免长时间的读锁等待,提升数据

数据库调优分层思想

数据库调优分层思想 1.调优策略 1)*号的处理(只提取必要字段,减少流量) 最好是用,有用的字段,减少流量. 表结构会改变,增加或者减少某列,如果*号全部查询出来 会造成代码逻辑错误. 2)大SQL(拆分,逐步缩小结果集) 大SQL执行起来非常耗时, where 后面带子句,或者读表联合查询. 或者临时表 暂时存储结果集 3)合理的索引(where子句后面的条件) 4)类型转换(‘’符号的使用) 在进行查询操作的时候把  ‘’带上 5)尽量不要用范围查询,或者缩小检索范围(程序逻辑update

Mysql数据库调优和性能优化

1. 简介 在Web应用程序体系架构中,数据持久层(通常是一个关系数据库)是关键的核心部分,它对系统的性能有非常重要的影响.MySQL是目前使用最多的开源数据库,但是mysql数据库的默认设置性能非常的差,仅仅是一个玩具数据库.因此在产品中使用MySQL数据库必须进行必要的优化. 优化是一个复杂的任务,本文描述MySQL相关的数据库设计和查询优化,服务器端优化,存储引擎优化. 2. 数据库设计和查询优化 在MySQL Server性能调优中,首先要考虑的就是Database Schema设计,和