SQL Server优化入门系列(二)—— 等待事件

俊达3年前技术文章1332

在上一篇文章中(SQL Server优化入门系列(一)——快速定位阻塞SQL),我们介绍了如何快速定位SQL Server中当前正在执行的SQL,以及被阻塞的SQL。


这里,我们将介绍如何通过等待事件来优化数据库。


说明

用户的SQL提交给服务器后,以TASK的形式运行。Task运行过程中,可能在多个状态之间转换,可能会等待某些资源(如IO、网络请求、锁、内存分配),通过等待事件,可以分析服务器在不同资源上消耗的时间,有助于了解系统运行状况,进行针对性的优化。


本文将介绍:

  • 如何查看系统中的当前top等待事件

  • 常见等待事件的含义和分析


SQL Server Task状态转换

SQL Server Task运行过程中,会在如下状态之间转换。

  • Running:运行中

  • Suspended:等待某个资源(如IO、锁、Latch、内存、网络请求)

    • 等待事物锁

    • 等待Latch

    • 等待IO请求

    • 等待网络请求

    • 等待内存分配

    • 等待checkpoint完成

    • 其他

  • Runnable: 等等执行(等待CPU)。表现为signal wait比较高。

  • Pending:等待worker线程


sqlserver_state.png



通过等待事件,可以了解SQL Server在不同的状态上消耗的时间。


等待事件分析


SQL Server提供了DMV视图,可用于分析等待事件

  • sys.dm_os_wait_stats:累计的各等待事件的等待时间。需要注意的是,这个DMV不包含当前处于等待中的事件。只有一个事件完成的时候,才会记录到该DMV。

  • sys.dm_os_waiting_tasks:当前处于等待状态的事件。



获取累计的等待事件信息

可通过如下SQL获取数据库的等待事件。需要注意的是,这里查到的是实例启动以来的累计值。

SELECT 
    SUM (waiting_tasks_count) AS waiting_tasks_count, 
    SUM (signal_wait_time_ms) AS signal_wait_time_ms, 
    SUM (wait_time_ms) AS wait_time_ms, 
    SUM (raw_wait_time_ms) AS raw_wait_time_ms
FROM 
(
    -- global server wait stats (completed waits only)
    SELECT 
        wait_type, 
        waiting_tasks_count, 
        (wait_time_ms - signal_wait_time_ms) AS wait_time_ms,  
        signal_wait_time_ms, 
        wait_time_ms AS raw_wait_time_ms
    FROM sys.dm_os_wait_stats
    WHERE waiting_tasks_count > 0 OR wait_time_ms > 0 OR signal_wait_time_ms > 0
    UNION ALL 
    -- threads in an in-progress wait (not yet completed waits)
    SELECT 
        wait_type, 
        1 AS waiting_tasks_count, 
        wait_duration_ms AS wait_time_ms, 
        0 AS signal_wait_time_ms, 
        wait_duration_ms AS raw_wait_time_ms
    FROM sys.dm_os_waiting_tasks
    -- Very brief waits quickly will roll into dm_os_wait_stats; we only need to 
    -- query dm_os_waiting_tasks to handle longer-lived waits. 
    WHERE wait_duration_ms > 1000
) AS merged_wait_stats
GROUP BY merged_wait_stats.wait_type;



获取增量的等待事件信息

一般情况下,我们需要查看数据库当前的等待情况,可以通过如下的方式获取:

drop table #wait_stat_start;
drop table #wait_stat_end;

SELECT 
	wait_type,
	CURRENT_TIMESTAMP as check_time,
    SUM (waiting_tasks_count) AS waiting_tasks_count, 
    SUM (signal_wait_time_ms) AS signal_wait_time_ms, 
    SUM (wait_time_ms) AS wait_time_ms, 
    SUM (raw_wait_time_ms) AS raw_wait_time_ms
into #wait_stat_start
FROM 
(
    -- global server wait stats (completed waits only)
    SELECT 
        wait_type, 
        waiting_tasks_count, 
        (wait_time_ms - signal_wait_time_ms) AS wait_time_ms,  
        signal_wait_time_ms, 
        wait_time_ms AS raw_wait_time_ms
    FROM sys.dm_os_wait_stats
    WHERE waiting_tasks_count > 0 OR wait_time_ms > 0 OR signal_wait_time_ms > 0
    UNION ALL 
    -- threads in an in-progress wait (not yet completed waits)
    SELECT 
        wait_type, 
        1 AS waiting_tasks_count, 
        wait_duration_ms AS wait_time_ms, 
        0 AS signal_wait_time_ms, 
        wait_duration_ms AS raw_wait_time_ms
    FROM sys.dm_os_waiting_tasks
    -- Very brief waits quickly will roll into dm_os_wait_stats; we only need to 
    -- query dm_os_waiting_tasks to handle longer-lived waits. 
    WHERE wait_duration_ms > 1000
) AS merged_wait_stats
GROUP BY merged_wait_stats.wait_type;

waitfor delay '00:00:10';

SELECT 
	wait_type,
	CURRENT_TIMESTAMP as check_time,
    SUM (waiting_tasks_count) AS waiting_tasks_count, 
    SUM (signal_wait_time_ms) AS signal_wait_time_ms, 
    SUM (wait_time_ms) AS wait_time_ms, 
    SUM (raw_wait_time_ms) AS raw_wait_time_ms
into #wait_stat_end
FROM 
(
    -- global server wait stats (completed waits only)
    SELECT 
        wait_type, 
        waiting_tasks_count, 
        (wait_time_ms - signal_wait_time_ms) AS wait_time_ms,  
        signal_wait_time_ms, 
        wait_time_ms AS raw_wait_time_ms
    FROM sys.dm_os_wait_stats
    WHERE waiting_tasks_count > 0 OR wait_time_ms > 0 OR signal_wait_time_ms > 0
    UNION ALL 
    -- threads in an in-progress wait (not yet completed waits)
    SELECT 
        wait_type, 
        1 AS waiting_tasks_count, 
        wait_duration_ms AS wait_time_ms, 
        0 AS signal_wait_time_ms, 
        wait_duration_ms AS raw_wait_time_ms
    FROM sys.dm_os_waiting_tasks
    -- Very brief waits quickly will roll into dm_os_wait_stats; we only need to 
    -- query dm_os_waiting_tasks to handle longer-lived waits. 
    WHERE wait_duration_ms > 1000
) AS merged_wait_stats
GROUP BY merged_wait_stats.wait_type;


sqlserver_wait_event.png


这里的等待事件中,有一部分是后台等待事件,一般不需要关注。需要关注的是前台等待事件。

当然,在实际运维过程中,我们会通过监控系统定时采集这些信息。


常见等待事件


下表列举了比较常见的等待事件,以及相关的优化方法

等待事件

描述

(可能的)优化方法

CXPACKET

出现CXPACKET说明有并行查询。需要同时查看其他top等待事件。

  • 查看并行查询的SQL是否能优化。

  • 调整参数max degree of parallelism,降低并行度或禁用并行查询。

  • 检查cost threshold for parallelism是否设置太低

SOS_SCHEDULER_YIELD

可能存在CPU资源不足的情况

  • 检查消耗大量CPU的SQL

  • 查看服务器CPU资源是否充足

THREADPOOL

等待worker线程

  • 检查是否worker线程不够

  • 是否有大量长时间运行的任务占用了worker

LCK_*

事物锁引起

  • 查看是否有事物未提交

  • 优化事物

  • 查看锁定的表是否缺少索引。

PAGEIOLATCH_*, IO_COMPLETION, WRITELOG

等待IO传输

  • 查看系统IO是否存在瓶颈(sys.dm_io_virtual_ file_stats )

  • 查看是否因为内存不足引起的IO请求频繁

  • 查看IO消耗高的SQL语句

PAGELATCH_*

Page争用

  • 查看是否系统的并发

  • 检查是否存在tempdb PFS, GAM, SGAM页面争用。添加多个tempdb文件。

LATCH_*



ASYNC_NETWORK_IO



OLEDB






相关文章

MySQL gh-ost DDL 变更工具

MySQL gh-ost DDL 变更工具

1. MDL 锁介绍MySQL 的锁可以分为四类:MDL 锁、表锁、行锁、GAP 锁,其中除了 MDL 锁是在 Server 层加的之外,其它三种都是在 InnoDB 层加的。下面主要介绍一下:MDL...

Kafka监控

1.监控健康状态为了了解 Kafka 的运作状态和性能状况需要对 Kafka 进行监控和诊断,通过Kafka提供的监控工具和插件可以诊断出 Kafka 的异常、错误、瓶颈和故障等问题并及时采取对应的措...

SQL Server优化入门系列(三)—— 性能计数器(performance counter)

SQL Server优化入门系列(三)—— 性能计数器(performance counter)

说明Performance Counter是windows系统中通用的性能分析工具。Windows OS和SQL Server暴露了很多Performance Counter,可用户分析整个系统的运行...

EasyMR之Yarn资源队列管理

EasyMR之Yarn资源队列管理

设想一下,你现在所在的公司有一套线上的hadoop集群。A部门经常做一些定时的BI报表,B部门则经常使用一些软件做一些临时需求。那么他们肯定会遇到同时提交任务的场景,这个时候到底如何分配资源满足这两个...

HBase导出表和备份表操作

HBase导出表和备份表操作

HBase提供了几种导出数据的方式,包括使用HBase自带的工具和使用HBase的API。本文主要是讲的使用HBase自带的工具进行导出首先我们创建一个表 插入一些数据hbase shelllistc...

Dockerfile全面指南:从基础到进阶,掌握容器化构建的核心工具

Dockerfile全面指南:从基础到进阶,掌握容器化构建的核心工具

引言        Dockerfile 是构建 Docker 镜像的核心文件。它定义了如何将应用程序及其依赖打包成一个可以跨平台运行的容器。本篇博客将从基础概...

发表评论    

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。