【巡检问题分析与最佳实践】RDS SQL Server CPU高问题

往期分享

RDS MySQL

RDS MySQL 实例空间问题

RDS MySQL 内存使用问题

RDS MySQL 活跃线程数高问题

RDS MySQL 慢SQL问题

RDS MySQL 实例IO高问题

RDS MySQL 小版本升级最佳实践

RDS PostgreSQL

RDS PostgreSQL 实例IO高问题

RDS PostgreSQL 慢SQL问题

RDS PostgreSQL CPU高问题

RDS SQL Server

RDS SQL Server 磁盘IO吞吐高问题


概述

CPU使用率过高问题是RDS SQL Server用户遇到的性能问题中较常见的一类。当RDS SQL Server实例的CPU使用率持续较高时,很容易导致数据库访问卡慢的情况,例如一些很简单的查询请求的响应时间也会很久甚至超时失败。

资源监控

RDS控制台

RDS SQL Server的控制台中提供了如下两种方式查看实例的CPU资源使用率情况。

监控与报警

在RDS控制台的“监控与报警”页中的“标准监控”->“资源监控”下,可以查看指定时间段内实例的CPU使用率信息。

【巡检问题分析与最佳实践】RDS SQL Server CPU高问题

对于单租户模式的RDS SQL Server实例(除RDS SQL Server 2008 R2本地盘版和RDS SQL Server所有版本的共享型实例之外)来说,这里的CPU使用率指的就是SQL Server服务进程的CPU资源消耗占操作系统总数的比例。

对于多租户模式的RDS SQL Server实例(包括RDS SQL Server 2008 R2本地盘版除独占主机类型外和RDS SQL Server所有版本的共享型实例)来说,同一个操作系统上会运行多个SQL Server服务进程,因此这里的CPU使用率实际上指的是该RDS实例对应的SQL Server服务进程的CPU资源消耗与该实例的计算规格所允许的最大CPU资源使用量之间的比例。

无论对于哪一种类型的RDS SQL Server实例来说,CPU使用率持续过高(例如持续大于80%或90%)通常都是一个较严重的性能问题,并很容易导致数据库查询卡慢的影响。而对于共享型实例来说,由于存在部分CPU核在不同实例之间共享复用的情况,即使当前实例本身的CPU使用率不是很高,也有可能遇到与之共享CPU资源的其他实例的CPU开销较高导致的CPU资源性能瓶颈问题。因此对数据库性能的稳定性要求较高的业务来说,应避免使用共享型的RDS实例。

另外在“监控与报警”页中可以设置监控频率,即监控数据显示的聚合粒度。对于CPU使用率指标来说,300秒/次的粒度是明显偏大的,通常建议设为60秒/次,也就是监控曲线中单个点的数据对应连续60秒内的平均值。

【巡检问题分析与最佳实践】RDS SQL Server CPU高问题

【巡检问题分析与最佳实践】RDS SQL Server CPU高问题

CloudDBA

在RDS控制台的“CloudDBA”->“性能优化”页中可以查看RDS SQL Server实例的各类性能指标,其中默认的第一项就是CPU使用率:

【巡检问题分析与最佳实践】RDS SQL Server CPU高问题

点击以上图片可以放大查看更细粒度的数据:

【巡检问题分析与最佳实践】RDS SQL Server CPU高问题

若默认没有显示CPU使用率指标的话,可以点击“自定义指标”按钮并在指标列表中选中CPU使用率项即可:

【巡检问题分析与最佳实践】RDS SQL Server CPU高问题

相比控制台监控与报警页中查看的监控数据,在CloudDBA的性能洞察页中显示的CPU使用率数据的粒度更细,为10秒一个聚合点。但性能洞察页中一次查看的性能监控数据时间范围不能超过2天,而监控与告警页中无此限制。

性能分析与优化

性能指标分析

对于偶发或突然出现的CPU使用率明显增高的情况,常见的原因有几类:

    • 数据库查询请求量突然增加。例如业务负载突然增加,或是数据缓存服务层出现了缓存穿透的情况等。
    • 查询请求的开销突然增大。例如应用中出现了一些新的类型的低效查询请求,或是某些查询语句的执行计划发生了改变等。
    • 查询语句的执行计划编译频率明显增加。例如当实例的缓存压力增大时,会导致执行计划缓存数量明显下降和缓存命中率下降,并进一步造成查询语句编译的频率和整体开销明显增加。

相应的,我们通常可以首先在性能指标监控中重点关注如下性能指标与CPU使用率指标之间的关系,以初步判断可能是哪种原因导致的CPU使用率突然增高:

【巡检问题分析与最佳实践】RDS SQL Server CPU高问题

它们分别对应SQL Server中的如下三个性能计数器指标:

    • SQLServer:SQL Statistics\Batch Requests/sec:这个指标即QPS,如果它的增高和CPU使用率的增高比较一致的话,则说明是数据库查询请求量的增加导致的CPU使用率增加,那么CPU高问题的原因本身并不在数据库层面,而应从应用层面分析是什么原因导致了数据库查询请求量的增加。
    • SQLServer:Buffer Manager\Page lookups/sec:这个指标是平均每秒在执行中的查询请求累积的总逻辑读页数,这个值如果较高则查询请求执行的CPU开销也一定会较高,并且导致Page lookup高的原因往往是查询语句的执行效率较差。因此如果Page lookup/sec的增高和CPU使用率的增高比较一致,而QPS值变化并不大的话,则通常说明是数据库中出现了查询语句执行开销高的情况,其中既有可能是出现了新的类型的低效查询,也有可能是原有的查询语句的执行计划发生了改变。这种情况下,就需要进一步分析是哪些类型的查询语句产生了较高的CPU资源消耗,并针对具体的查询语句进行性能优化。
    • SQLServer:SQL Statistics\SQL Compilations/sec:这个指标是平均每秒的查询请求编译的次数,如果SQL Compilations/sec的增高和CPU使用率的增高比较一致,而QPS值变化不大的话,则有可能是查询编译开销高导致的CPU增高。这时还可以进一步检查一下如下与执行计划缓存数量相关的性能指标,如果Cache_Object_Counts和Cache_Pages的值下降也比较明显的话,则有较大可能是实例的缓存压力大原因所致。这种情况下,提升实例的内存规格通常是比较有效的优化方法。

【巡检问题分析与最佳实践】RDS SQL Server CPU高问题

以下为一个实际的案例参考:

【巡检问题分析与最佳实践】RDS SQL Server CPU高问题

从CPU使用率指标的监控中可以看到,CPU的飙高主要出现在9:10-9:20和9:30-9:40这两个时段。该实时段内实例的QPS并没有增加,QPS的增加实际上是在9:40之后,因此CPU利用率的增高并不是数据库查询请求数量的增加导致的。同期SQL Compilations/sec的值也无明显飙升,并且其绝对值也很低,因此查询编译开销也不是导致CPU增高的原因。而Page lookups/sec值的增高与CPU使用率的增高时间基本一致,因此较大的可能性是9:10-9:20和9:30-9:40这两个时段内有某些执行开销较高的查询请求存在,导致了实例整体CPU使用率的明显飙升。

在这种情况下,我们就需要进一步去分析在上述时段内主要有哪些查询语句的执行导致了较高的CPU资源消耗。另外Page lookups/sec的值增高一定会导致CPU利用率增高,但也会有些查询语句的执行CPU开销很高而逻辑读开销并不高的情况,这时我们也是要先去分析CPU高时段内的查询语句的执行信息以定位原因。

查询语句性能分析

活动会话分析

在导致SQL Server实例的CPU使用率突然增高的各种原因中,最常见的情况还是数据库中出现了某些执行效率较差的查询语句,并造成语句执行过程中的CPU资源消耗较高。对于这类查询语句性能问题的定位和分析,主要可以利用CloudDBA性能洞察中的Average Active Sessions(AAS)功能。

【巡检问题分析与最佳实践】RDS SQL Server CPU高问题

RDS后台会每10秒检查一次SQL Server实例中的活动会话(Active Session)的信息,并记录下当前处于活动状态的查询请求的SQL语句、query hash、执行计划及等待事件类型等。对于CPU开销高的查询语句来说,它在处于执行状态的过程中有很大可能其等待类型会是CPU,这样在性能洞察的AAS部分的top SQL列表中,等待类型是CPU的占比较高的语句,基本上也就是对CPU资源消耗占比较高的语句了。

这里的SQL Hash列的值即对SQL语句结构参数化之后的哈希值,它用于标识在语句结构上完全相同的一类SQL语句,对于将SQL语句按照结构进行归类聚合统计提供了便利,利用它可以直接从系统视图sys.dm_exec_query_stats中基于query_hash列的值进行检索,从而获得该语句最新的执行情况统计的信息。此外在上图中直接点击语句的SQL Hash列的链接,还可以查看该语句本身的AAS统计结果:

【巡检问题分析与最佳实践】RDS SQL Server CPU高问题

对于CPU开销高的语句的性能问题分析与优化,主要还是通过分析其执行计划来进行。在以上top SQL列表中,可以直接点击“分析”查看SQL语句的执行计划,或是点击“下载”将其执行计划下载到本地(扩展名为.sqlplan),并在SQL Server Management Studio工具中打开并查看详细信息。

点击“分析”之后,除了可以查看到SQL语句的执行计划,还可以看到CloudDBA基于对语句的执行计划的分析给出的一些性能优化参考建议:

【巡检问题分析与最佳实践】RDS SQL Server CPU高问题

【巡检问题分析与最佳实践】RDS SQL Server CPU高问题

不过以上优化建议主要基于一些常规的基本优化策略,对于结构较为简单的SQL语句来说,效果往往会比较好。但对于一些较为复杂的SQL语句来说,建议用户还是应在参考以上优化建议的基础上对执行计划的信息进行具体的分析,从而得出优化方案并做实际测试验证。

Top SQL分析

利用CloudDBA性能洞察中的活动会话监控功能,可以比较方便的定位在特定时段内导致CPU资源使用率飙升的问题SQL语句。但这种方式并不能提供各类SQL语句的执行频率、平均CPU开销及整体CPU资源消耗占比等信息。从优化实例的整体CPU资源效率的角度考虑,获取CPU资源消耗top SQL语句的详细统计信息往往是很有必要的。

SQL Server中对于SQL语句和存储过程等对象的执行相关的信息提供了自动汇总统计的功能,并可通过sys.dm_exec_query_stats和sys.dm_exec_procedure_stats等系统视图直接查看,对于定位各类资源开销的top SQL语句是非常方便的。视图中的worker_time相关的列即是关于SQL语句执行的实际CPU开销统计的,例如以下语句可以用于统计自SQL Server服务启动以来,当前实例上总体CPU开销排名前10的SQL语句的执行统计情况,也包括其最后缓存使用的执行计划的信息等。

select top 10
DB_NAME(qp.dbid) as database_name, OBJECT_NAME(qp.objectid,qp.dbid) as object_name,
SUBSTRING(st.text,(qs.statement_start_offset/2)+1,((case qs.statement_end_offset when -1 then DATALENGTH(st.text) else qs.statement_end_offset end - qs.statement_start_offset)/2) + 1) as sql_statement 
,qp.query_plan
,qs.sql_handle,qs.statement_start_offset,qs.statement_end_offset,qs.plan_generation_num,qs.plan_handle,qs.creation_time,qs.last_execution_time,qs.execution_count,qs.total_worker_time,qs.last_worker_time,qs.min_worker_time,qs.max_worker_time,qs.total_physical_reads,qs.last_physical_reads,qs.min_physical_reads,qs.max_physical_reads,qs.total_logical_writes,qs.last_logical_writes,qs.min_logical_writes,qs.max_logical_writes,qs.total_logical_reads,qs.last_logical_reads,qs.min_logical_reads,qs.max_logical_reads,qs.total_elapsed_time,qs.last_elapsed_time,qs.min_elapsed_time,qs.max_elapsed_time
from sys.dm_exec_query_stats qs 
cross apply sys.dm_exec_sql_text(sql_handle) st
cross apply sys.dm_exec_query_plan(plan_handle) qp
order by qs.total_worker_time desc

关于系统视图sys.dm_exec_query_stats的更多详细说明,可参考如下链接:

https://docs.microsoft.com/en-us/sql/relational-databases/system-dynamic-management-views/sys-dm-exec-query-stats-transact-sql

此外Management Studio中自带的Top query类报表,以及CloudDBA性能优化中的TOP SQL/TOP Objects报表功能,实际也是基于以上系统视图的,使用起来较为方便,但是不如直接基于系统视图进行查询的方式更加灵活。

【巡检问题分析与最佳实践】RDS SQL Server CPU高问题

【巡检问题分析与最佳实践】RDS SQL Server CPU高问题

参数优化

SQL Server实例级的参数选项max degree of parallelism(最大并行度,简称MAXDOP)用于控制单个查询请求可以同时使用的最大活跃线程数(也即CPU核数)。对于一些CPU开销较高的SQL语句来说,若使用并行度较高的执行计划,其执行时间可能会显著缩短,但也意味着其在单位时间内的CPU资源消耗会明显增加,并可能由此导致实例的CPU使用率在短时间内大幅飙升。

最大并行度值的设置,往往要在提升低效查询语句的执行速度和保持实例的CPU资源使用率整体平稳之间进行权衡。一般来说,对于查询请求的并发量较高且绝大部分SQL语句的执行开销都很低的OLTP型负载的实例来说,MAXDOP的值应设的较小一些,甚至为1(即完全无并行)。而对于查询请求的并发量较低且存在一些执行开销较高的SQL语句的OLAP型或混合型负载的实例来说,MAXDOP的值可以设的相对大一些,但一般建议不超过实例可使用的最大CPU核数的1/2或1/4。

另外查询并行度的提升,虽然可以帮助提高某些SQL语句的执行速度,但往往也会带来语句执行过程中的整体CPU资源开销的增加,并导致实例的整体CPU使用率的增加。因此对于实例的整体CPU使用率不高的情况,MAXDOP的值通常可以设的相对高一些。而对于实例的整体CPU使用率已经很高的情况,增大MAXDOP往往会进一步加剧CPU资源的竞争,并导致查询语句性能的整体下降,这种情况下MAXDOP的值通常就应设的低一些。

在RDS SQL Server中,max degree of parallelism参数的默认值为2,是一个相对平衡偏保守的选择。用户可以通过RDS专用的存储过程sp_rds_configure来对该参数的设置值进行修改,并且是立即生效的,无需重启实例:

https://help.aliyun.com/document_detail/88094.html#section-exf-v53-v2b

当出现实例的CPU使用率过高的情况时,如果从CloudDBA性能洞察的AAS中看到Parallelism类型等待的占比较高,则可以考虑适当降低max degree of parallelism参数的设置值来缓解实例的CPU压力。

上一篇:【巡检问题分析与最佳实践】Redis 流控问题


下一篇:【巡检问题分析与最佳实践】RDS PostgreSQL CPU高问题