MySQL CPU 使用率高的原因和解决方法

Nov 1, 2018 00:00 · 1557 words · 4 minute read MySQL SQL

1. 常见原因

系统执行应用提交查询(包括数据修改操作)时需要大量的逻辑读(逻辑 IO,执行查询所需访问的表的数据行数),所以系统需要消耗大量的 CPU 资源以维护从存储系统读取到内存中的数据一致性。

说明:大量行锁冲突、行锁等待或后台任务也有可能会导致实例的 CPU 使用率过高,但这些情况出现的概率非常低,本文不做讨论。

系统资源、语句执行成本以及 QPS(Query Per Second 每秒执行的查询数)之间的关系:

  • 条件:应用模型恒定(应用没有修改)。
  • avg_lgc_io:执行每条查询需要的平均逻辑 IO。
  • total_lgc_io:实例的 CPU 资源在单位时间内能够处理的逻辑 IO 总量。
  • 关系公式:total_lgc_io = avg_lgc_io x QPS - 单位时间 CPU 资源 = 查询执行的平均成本 x 单位时间执行的查询数量

2. 解决方法

避免出现 CPU 使用率达到 100% 的一般原则

  • 设置 CPU 使用率告警,实例 CPU 使用率保证一定的冗余度。
  • 应用设计和开发过程中,要考虑查询的优化,遵守 MySQL 优化的一般优化原则,降低查询的逻辑 IO,提高应用可扩展性。
  • 新功能、新模块上线前,要使用生产环境数据进行压力测试。
  • 新功能、新模块上线前,建议使用生产环境数据进行回归测试。

3. 典型示例

3.1. 应用负载(QPS)高

现象描述

  • 特征实例的 QPS(每秒执行的查询次数)高,查询比较简单、执行效率高、优化余地小
  • 表现没有出现慢查询(或者慢查询不是主要原因),且 QPS 和 CPU 使用率曲线变化吻合
  • 常见场景:该状况常见于应用优化过的在线事务交易系统(例如订单系统)、高读取率的热门 Web 网站应用、第三方压力工具测试(例如 Sysbench)等。

解决方案

对于由应用负载高导致的 CPU 使用率高的状况,使用 SQL 查询进行优化的余地不大,建议从应用架构、实例规格等方面来解决,例如:

  • 升级实例规格,增加 CPU 资源。
  • 增加只读实例,将对数据一致性不敏感的查询(比如商品种类查询、列车车次查询)转移到只读实例上,分担主实例压力。
  • 对于查询数据比较静态、查询重复度高、查询结果集小于 1 MB 的应用,考虑开启查询缓存(Query Cache)。
  • 定期归档历史数据、采用分库分表或者分区的方式减小查询访问的数据量。
  • 尽量优化查询,减少查询的执行成本(逻辑 IO,执行需要访问的表数据行数),提高应用可扩展性。

3.2. 查询执行成本(查询访问表数据行数 avg_lgc_io)高

现象描述

  • 特征实例的 QPS(每秒执行的查询次数)不高;查询执行效率低、执行时需要扫描大量表中数据、优化余地大
  • 表现存在慢查询,QPS 和 CPU 使用率曲线变化不吻合
  • 原因分析:由于查询执行效率低,为获得预期的结果即需要访问大量的数据(平均逻辑 IO高),在 QPS 并不高的情况下(例如网站访问量不大),就会导致实例的 CPU 使用率高。

解决方案

解决该状况的原则是:定位效率低的查询、优化查询的执行效率、降低查询执行的成本。

操作步骤

root 账户登录后通过 show processlist;show full processlist; 命令查看当前执行的查询。

对于查询时间长、运行状态(State 列)是“Sending data”、“Copying to tmp table”、“Copying to tmp table on disk”、“Sorting result”、“Using filesort”等都可能是有性能问题的查询。

USE mysql 选择 mysql 系统数据库,这里要使用的是 slow_log 慢查询表。MySQL 系统将响应时间超过阈值的语句记录在慢查询表中。阈值可以自行设置,日志可以写入文件或数据表,如果对性能要求高的话,建议写文件。

默认情况下,MySQL 不开启慢查询日志,long_query_time 的默认值为10(即10秒,通常设置为1秒),即运行10秒以上的语句是慢查询语句。

一般来说,慢查询发生在大表(比如:一个表的数据量有几百万),且查询条件的字段没有建立索引(优化),此时,要匹配查询条件的字段会进行全表扫描,耗时超过阈值的就是慢查询语句。

当定位到效率低的查询后,可以使用 EXPLAIN 关键字来显示处理查询的详细过程。