首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

揭秘数据库性能杀手-等待

在上一篇文章《揭秘数据库性能杀手 - 等待》中提到,等待是揭秘数据库性能故障的有效手段。

比如下面的 5 分钟采样图,可以帮我们解析平均等待 CPU 执行时间:

此图展示了平均等待 CPU 执行的时间是均衡的,会有一个峰值,但是会回落到正常区间。最大等待也不会超越总计等待时间的 10.82%.

再次回顾下,查询的生命周期:等待,执行,等待,执行。过程中会经历这些状态:

Running - 正在 CPU 上执行

Runnable - 等待分配 CPU 的执行时间,此时 CPU 正在执行其他 Query 而无暇分配执行资源

Suspended - 等待资源的分配,等到资源分配之后,重新进入 CPU 队列,等待 CPU 执行

只有在 Suspended 状态下,是在等待除了 CPU 资源外,其他系统资源的释放。

平均等待 CPU 执行时间,当然是越小越好。但实际情况,总是在一个范围内波动。一旦超过这个波动范围,我们就要着手分析,超出的原因。怎么分析,我的下篇文章《用好三个 DMV, 检测 80%的性能故障问题》中会着重讲解。

这篇文章,预先跟大家分享下捕获性能指标用到的脚本,以及捕获性能数据之后,怎么来分析。

脚本是开了一个窗口,连续 5 分钟的来获取性能数据。如果 5 分钟太频繁,影响系统性能了(这会在性能指标展示图中,分析出来,我们的捕获动作是不是对系统性能影响过大),就设置的时间间隔再长一些,并且仅捕获一些常用的性能指标数据。

CREATE TABLE T_GLOBAL_WAIT_COLLECT_PROGRESS_CONTROL

(ENABLED_FLAG INT ) ;

INSERT INTO T_GLOBAL_WAIT_COLLECT_PROGRESS_CONTROL(ENABLED_FLAG) VALUES(1);

CREATE TABLE T_GLOBAL_WAIT_STATS_HISTORY

(

CHECKPOINT_SEQUENCE BIGINT IDENTITY(1,1)

,WAIT_TYPE NVARCHAR(60)

,WAITING_TASKS_COUNT BIGINT

,WAIT_TIME_MS BIGINT

,MAX_WAIT_TIME_MS BIGINT

,SIGNAL_WAIT_TIME_MS BIGINT

,CHECKPOINT_DATETIME DATETIME

)

DECLARE @INC_TIME_PERIOD INT = 5 ;

DECLARE @WAIT_DELAY VARCHAR(20) = '' ;

SET @WAIT_DELAY = LEFT( '00:' + RIGHT('00'+CONVERT(VARCHAR,@INC_TIME_PERIOD),2),5 )

WHILE(EXISTS(SELECT * FROM T_GLOBAL_WAIT_COLLECT_PROGRESS_CONTROL WHERE ENABLED_FLAG=1))

BEGIN

INSERT INTO T_GLOBAL_WAIT_STATS_HISTORY(WAIT_TYPE

,WAITING_TASKS_COUNT

,WAIT_TIME_MS

,MAX_WAIT_TIME_MS

,SIGNAL_WAIT_TIME_MS

,CHECKPOINT_DATETIME

)

SELECT wait_type

,waiting_tasks_count

,wait_time_ms

,max_wait_time_ms

,signal_wait_time_ms

,getutcdate()

FROM sys.dm_os_wait_stats WITH(NOLOCK)

WHERE waiting_tasks_count + wait_time_ms + max_wait_time_ms + signal_wait_time_ms 0

WAITFOR DELAY @WAIT_DELAY ;

END

; WITH BASE_QUERY AS (

SELECT CHECKPOINT_DATETIME, ROW_NUMBER() OVER ( ORDER BY CHECKPOINT_DATETIME) AS RNK

FROM (SELECT DISTINCT CHECKPOINT_DATETIME FROM T_GLOBAL_WAIT_STATS_HISTORY WITH(NOLOCK) ) TMP

)

, CAL_QUERY AS ( SELECT HIS.*, BQ.RNK

FROM T_GLOBAL_WAIT_STATS_HISTORY HIS

INNER JOIN BASE_QUERY BQ ON HIS.CHECKPOINT_DATETIME = BQ.CHECKPOINT_DATETIME

)

SELECT CHECKPOINT_DATETIME,

CONVERT(NUMERIC(9,2),

100.00* SUM( SIGNAL_WAIT_TIME_MS ) / SUM( WAIT_TIME_MS) )

FROM CAL_QUERY

GROUP BY CHECKPOINT_DATETIME

这就是上图中用到的脚本了。

平均占比有时候能看到全局的波动,掌握大趋势。但对细小的波动可能就会略去,尤其是平均数还会掩盖小细节的异常

比如下图所示,其实每种 wait 都在剧烈的变化着,如果我们忘记观察他们,就很难得到诸如客户端访问异常的情况,比如有大量的读在被客户调用:

红色波峰其实是 Async_IO_COMPLETION 等待的异常。

(图中线非常多,多亏 Excel 能即点即示。你能很快找到你感兴趣的那几条线。要追求完美,自己动手写 D3.js 吧)

Async_IO_COMPLETION 是由于客户端接收数据能力偏弱造成的。可能的原因是客户端在按批次处理数据,但批次量太多,而每批次的数据量客户端处理繁忙,把剩余数据留在服务器端等待客户端接收。这一般是报表抓取了过多的数据,或者开发在帮忙导大批量数据(很多开发都会有头痛的帮用户抓取数据的情况)。

针对这种情况,就要想办法善后了,尤其在业务系统繁忙的情况下,大量数据抓取极大影响了用户体验。这张图就是帮你开脱的好帮手。

上图脚本如下:

WITH BASE_QUERY

AS (

SELECT CHECKPOINT_DATETIME

,ROW_NUMBER() OVER (

ORDER BY CHECKPOINT_DATETIME

) AS RNK

FROM (

SELECT DISTINCT CHECKPOINT_DATETIME

FROM T_GLOBAL_WAIT_STATS_HISTORY WITH (NOLOCK)

) TMP

)

,CAL_QUERY_CURRENT

AS (

SELECT HIS.*

,BQ.RNK

FROM T_GLOBAL_WAIT_STATS_HISTORY HIS

INNER JOIN BASE_QUERY BQ ON HIS.CHECKPOINT_DATETIME = BQ.CHECKPOINT_DATETIME

)

,CAL_QUERY_PREVIOUS

AS (

SELECT HIS.*

,BQ.RNK

FROM T_GLOBAL_WAIT_STATS_HISTORY HIS

INNER JOIN BASE_QUERY BQ ON HIS.CHECKPOINT_DATETIME = BQ.CHECKPOINT_DATETIME

)

,CAL_QUERY_DETAL

AS (

SELECT CUR.WAIT_TYPE

,CUR.SIGNAL_WAIT_TIME_MS - PRE.SIGNAL_WAIT_TIME_MS AS DELTA

,CUR.CHECKPOINT_DATETIME

,CUR.WAIT_TIME_MS - PRE.WAIT_TIME_MS AS DELTA_WAIT

FROM CAL_QUERY_CURRENT CUR

INNER JOIN CAL_QUERY_PREVIOUS PRE ON CUR.WAIT_TYPE = PRE.WAIT_TYPE

AND CUR.RNK = PRE.RNK + 1

)

SELECT TOP 5000 *

FROM CAL_QUERY_DETAL

WHERE DELTA > 0

ORDER BY CHECKPOINT_DATETIME DESC

除了捕获sys.dm_os_wait_stats 的数据,还可以使用 sql trace, extended events, sql profiler 等手段,同样也在《用好三个 DMV, 检测 80%的性能故障问题》中会着重讲解。

感谢阅读与分享,欢迎关注【有关SQL】公众号

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180121G0OMFB00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券