PolarDB MySQL版CPU使用率高

CPU作为数据库最核心的资源,是日常运维中需要重点关注的对象。CPU用满,会导致应用RT增高、业务卡顿,更严重会导致数据库实例hang死、发生HA等问题,严重影响现网业务。正常情况下,对于CPU的监控需要设定安全水位,超出安全水位时要及时进行处理,否则会引发不可预期的严重后果。

现网业务中的CPU使用率

随着业务的增长,数据库集群的规格可能已经不能满足业务流量的上涨需求。此时由于流量的不断增长,数据库集群的使用率逐渐提升,CPU使用率也逐渐升高。如果从性能曲线进行观察,必然存在某个指标(如QPS/IOPS)呈上涨趋势,与CPU使用率上涨趋势相似。如下图所示:

图 1. QPS(每秒请求数)图1 QPS(每秒请求数)

图 2. CPU使用率CPU使用率

此时,如果CPU出现瓶颈,基本可以断定数据库集群规格已不足以支撑当前的业务流量,此时最好对数据库集群增加只读节点或者扩容集群规格。

  • 如果大部分业务场景都是读请求,可以通过增加只读节点,进行集群横向扩容,以分流读请求。具体操作请参见增加或删除节点

  • 如果大部分业务场景都是写请求,此时增加只读节点不会对性能起到提升作用,需要对集群规格进行扩容变更,例如:由4C规格变更为8C规格。具体操作请参见手动变配

非预期内CPU使用率增长

导致CPU使用率非预期增长的情况比较复杂,本文主要针对比较常见的几个现象:慢查询、活跃线程高、内核配置不合理、系统BUG进行说明。

  • 慢查询

    正常情况下,CPU使用率的增长,一般是由于SQL语句不合理,产生了慢查询,同时活跃线程堆积导致CPU使用率过高。但是一定要区分清楚,是由于慢查询导致的CPU使用率高,还是由于其他资源打满查询变慢导致的CPU使用率高。

    您可以在PolarDB控制台慢SQL菜单中,查看慢查询情况,具体请参见慢SQL

    如果慢查询中有数据,就需要对慢查询进行分析。如果在慢日志明细页签中,扫描行远远大于返回行数,则说明是慢查询导致的CPU使用率过高。慢日志明细

    说明

    此处主要针对TP类查询进行分析,需要排除掉count类查询,一些AP类查询的扫描行数确实很大。

    TP类查询业务的读写数据量都非常小,如果某个查询的扫描数据量非常大,那么大概率是由于索引缺失导致。例如下述查询语句在慢查询列表中显示扫描数据量为1万+,但返回数据为1条,那么很明显在name列上有索引缺失的情况。

    SELECT * FROM table1 WHERE name='testname';

    那么可以通过下述语句确认在name列上是否有索引。

    SHOW index FROM table1;
    • 如果name列上没有索引,可以通过下述语句添加索引列,消除此类大数据量扫描导致的慢查询。

      ALTER TABLE table1 ADD KEY ix_name (name);
    • 如果name列上有索引,可以通过下述语句查看SQL语句的执行计划,确认是否使用了正确的索引。

      EXPLAIN SELECT * FROM table1 WHERE name='testname';

      如果发现name列有索引,但没有被使用,有可能是出现了统计信息不准确导致生成了错误的执行计划。可以通过下述语句重新生成表上的统计信息用以纠正错误计划。

      ANALYZE TABLE table1;

      执行完成后,再通过下述语句确认是否使用了正确的索引。

      EXPLAIN SELECT * FROM table1 WHERE name='testname';
  • 活跃线程高

    活跃线程高一定会带来CPU使用率的增长。抽象来说,MySQL实现中,每一个CPU只能在同一时间内处理一个请求。假设是16C规格的集群,最多只能同时处理16个请求。但是要注意,这里的请求指的是内核层面,而非应用的并发层面。

    如果排除掉慢查询导致的请求无法正常处理,活跃线程堆积一般都是由于现网业务流量增长造成的。通过查看性能曲线,如果整体流量以及请求趋势和活跃线程的堆积趋势一致,那么说明集群资源已经达到上限,此时需要通过对数据库集群增加只读节点或者扩容集群规格来解决。

    需要注意的是,在活跃线程达到临界点时,可能在CPU层面开始产生争抢,内核中会产生大量的mutex排他锁,此时性能曲线表现特征为高CPU使用率、高活跃线程、低IO或低QPS。另外一种情况是突然的业务洪峰,建立连接速度非常快,也可能在CPU层面产生大量争抢,从而导致请求堆积。此类问题一般可以通过开启集群的thread_pool特性进行流控缓解,具体请参见Thread Pool。如果活跃线程有所缓解,同时还要注意应用侧是否已经产生了业务堆积,如果CPU负载较高同时活跃线程依然高居不下,此时则同样要考虑是不是对集群进行扩容操作。

    另有一种情况是前端连接风暴导致集群流量瞬间堆积,此时流量属于异常流量,一般出现在流量数据被爬虫拉取数据的场景下。此时可以通过SQL限流的方式进行请求拒绝,具体操作请参见会话管理

  • 内核配置不合理

    自建MySQL的参数是通用场景下的标准配置,对于个别业务可能不太适用,需要进行微调。有些问题在业务上线初期数据量较少的情况下不会触发,但是随着时间的推移,业务数据量增大,在特定条件下有些问题就会暴露出来。

    比较常见的问题会出现内存使用争抢。在MySQL体系中,内存主要作为数据缓存使用,也就意味着数据需要不断的迭代,最常用是buffer poolinnodb_adaptive_hash_index内存区域。整个数据库系统的缓存区域,是数据交换最为频繁的位置,如果内存不足和内存页争抢,则会出现各种异常的堆积和慢查询。最典型的表现是数据库突然CPU上涨打满,并且出现慢查询。经过排查后发现该问题并非索引缺失,这个时候就有可能是内存系统发生了问题。

    例如:在进行truncate table操作时,MySQL要遍历buffer pooltruncate表的数据页全部驱逐。此时大规格的集群中,如果innodb_buffer_pool_instances配置为1并且并发相对较高的情况下,就有可能出现争抢问题。这个问题在业务上线初期就可以发现,一般来说可以将innodb_buffer_pool_instances的取值与CPU核数对齐,将buffer pool进行分桶,就可以规避此类问题。

    另外一种场景是innodb_adaptive_hash_index出现争抢,比较明显的表现是执行下述命令时会出现大量的hash0hash.cc等待。

    SHOW ENGINE innodb STATUS;

    在AHI显示段中,会出现明显的数据倾斜。如下图所示:AHI显示段此类问题可以将innodb_adaptive_hash_index参数关闭,也就是直接弃用AHI特性,已有数据表明在混合读写的场景下AHI也有可能带来负面的性能影响,关闭后对整体业务的影响不是很大。

  • 系统BUG

    系统BUG是相对少见的问题,例如比较早的进程死锁、表上统计信息置0导致全表扫描等。随着产品的快速迭代,系统BUG导致的CPU问题相对不多。但是由于排查问题时,涉及更多内核层面的信息,用户自行处理可能有一定的难度,建议提交工单联系阿里云技术支持进行排查。