news 2026/9/19 23:03:01

MySQL CPU飙升排查与优化实战:从原理到命令全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MySQL CPU飙升排查与优化实战:从原理到命令全解析

数据库CPU被打满,应该是所有后端开发、DBA以及运维同学都经历过的噩梦。尤其是线上业务报障,看到告警群里“MySQL CPU使用率 > 95%”的红色消息,心跳都得漏半拍。很多人在这一步容易慌,上来就重启数据库或者直接kill掉一堆进程,运气好救回来了,运气不好直接把事务打断、数据搞乱,然后面对研发和业务的口诛笔伐。

其实MySQL CPU使用率异常的排查非常有套路,只要掌握了正确的方法论,大概率半小时内能定位到根因,最长也就一两个小时。这篇文章我结合自己多年处理这类故障的经验,从“为什么CPU会高”的本质讲起,再把完整的排查链路、核心命令、优化方法以及我踩过的坑全部摊开来讲一遍。不管你是刚入行被拉去救火的初级运维,还是已经带了团队的技术负责人,这套思路几乎可以直接复制到你自己的故障处理流程里。

1. 先搞懂MySQL的CPU被谁吃掉了

很多同学一看到CPU高就忙着加内存、加CPU或者开一堆缓存,这些东西不能说完全没用,但方向大概率是错的。在动任何“治疗手段”之前,必须先搞清楚一个根本问题:MySQL进程的CPU时间片到底消耗在哪一步?

1.1 从表象到底层,CPU时间片的去向

MySQL是一个多线程架构的数据库服务,主线程负责接收网络连接,然后为每个客户端连接分配一个线程来处理请求。当服务端执行一条SQL时,CPU主要消耗在以下几个环节:

  • 语法解析与优化器计算:每一条SQL进入MySQL后,都要经过词法解析、语法树生成、预处理、查询优化器计算执行计划等一系列步骤。复杂的SQL、子查询嵌套、大量Join条件,都会导致优化器需要计算大量可能的执行路径,CPU在这里会被明显消耗。
  • 存储引擎层的查找与扫描:InnoDB引擎在读取数据时,需要遍历B+树索引、判断记录是否满足条件、进行行的可见性检查(MVCC)。当索引失效或者压根没有索引时,就是全表扫描,一条一条记录读出来比对,数据量大时CPU和磁盘IO都会爆掉。
  • 排序与临时表操作:ORDER BY、GROUP BY、DISTINCT、UNION这类操作,如果数据量超过了sort_buffer_size或者临时表内存阈值,MySQL会把数据放到磁盘临时表。这里不只是磁盘IO问题,排序本身是一个极其消耗CPU的过程。
  • 连接线程的上下文切换:当并发连接数非常高时,操作系统的线程调度会频繁进行上下文切换。每一次切换CPU都要保存当前线程的状态、加载下一个线程的状态,几百上千个连接同时活跃,大量的CPU时间其实浪费在线程切换上,真正执行SQL的时间反而很少。

这里可以用一个比较通俗的类比来理解。数据库执行SQL就像一个人在图书馆找书,如果你手里有索书号(索引),直接走到对应书架取出书,整个过程很快;如果没有索书号,就只能从第一排书架开始一本一本翻(全表扫描),翻到最后一排才找到目标。这个“翻书”的过程就是CPU和IO在疯狂工作。

1.2 CPU高并不等于业务量大,先给问题定性

遇到CPU告警,我的习惯是先给问题定性,而不是急着查SQL。定性可以分为以下几个维度:

  • 是持续高还是间歇性飙高?持续高通常说明有慢SQL在全时段地跑,或者负载打满;间歇性飙高往往是某个定时任务、报表查询在固定时间点触发,或者连接数达到了某个阈值后产生雪崩。
  • 是整个机器CPU高,还是MySQL进程单独高?如果一个8核的机器CPU整体打满,MySQL占700%,说明MySQL自身就是元凶;如果MySQL只占100%,其他进程占了300%,那问题可能在别的服务上,不要误伤数据库。
  • 是从数据库升级后开始的,还是新上的SQL导致的?这个可以通过查看最近的上线记录、慢查询日志的增长趋势来判断,往往能最快缩小范围。

以前我遇到过一例非常典型的误判。某次告警显示MySQL所在机器CPU持续100%,第一时间去查慢查询日志,发现确实有很多慢SQL,于是开始优化SQL,折腾了一个小时没见效。后来仔细观察top输出,才发现一个日志采集Agent在疯狂写文件,MySQL进程本身只有不到30%的CPU占用。所以先通过top -Hp查看MySQL进程内部线程情况,别上来就甩锅给SQL,这是第一个要养成的习惯。

2. 核心排查链路:从进程到SQL的层层下钻

这一章是全文的核心,我把整套排查命令整理成了一条从上到下、层层深入的链路。每一次CPU告警,我都会按这个顺序执行,基本能定位到具体某条SQL或者某个参数。

2.1 第一步:用top和vmstat判断瓶颈边界

登录到数据库服务器后,第一步不是进MySQL,而是先在操作系统层面做体检。执行top命令,重点关注以下几项:

  • %Cpu(s)整体的用户态、内核态以及等待IO的比例。如果sy也就是内核态占比很高,说明大量CPU时间消耗在系统调用和线程切换上,这种情况通常和连接数过高、小事务并发太多有关。
  • load average的值,如果大于CPU核数的5到10倍,说明系统已经严重过载。
  • 进程列表中mysqld进程的CPU占用率,确认是不是MySQL自身在消耗CPU。

top只是第一眼印象,要看得更细可以用vmstat 1 5,每秒采样一次、连续采样5次,观察r(运行队列长度)、us(用户态CPU占比)、sy(内核态CPU占比)、wa(等待IO占比)这几个字段。如果wa很高,意味着大量时间在等待磁盘IO,这时候SQL往往是在做全表扫描或者排序落到磁盘临时表;如果us很高,说明CPU确实在全力计算,比如大量聚合运算、正则匹配、行数统计等;如果sy很高,就要怀疑连接数过多导致的上下文切换。

补充一个我平时很喜欢用的排查工具组合:pidstat -t -p $(pgrep mysqld | head -1) 1,这个命令可以按线程显示mysqld进程内部的线程CPU占用。如果只有个别线程CPU很高,说明是某条SQL或某个特定操作导致的;如果所有线程都高,则要考虑系统层面的问题,比如内存不足导致页交换、磁盘故障等。

2.2 第二步:连进MySQL,用show processlist抓现行

操作系统层面确认MySQL是元凶后,立刻登录MySQL,执行经典命令:

SHOW FULL PROCESSLIST;

这个命令会把当前所有连接到MySQL的会话线程信息列出来,重要的字段有IdUserHostdbCommandTimeStateInfo

排查时要重点看两类线程:一是Time很长、StateSending dataSorting resultCopying to tmp table的查询线程,这些是正在执行慢查询的SQL;二是大量CommandSleep的线程,如果这类空闲连接数量几百上千,虽然单个不消耗CPU,但大量连接本身就会带来调度开销。

Info字段展示了当前正在执行的SQL语句,不过可能不完整,可以在拿到SQL后用下面的方式补全:

SELECT * FROM information_schema.processlist WHERE id = 指定连接ID\G

拿到具体的SQL之后,不要急着复制出来测试,先在脑海里做一个粗略判断:这个SQL的查询条件是否命中了索引?是否对索引列做了函数计算?Join的表数量和顺序是不是合理?如果一眼就能看出问题,直接进入后面的优化阶段;如果看不出问题,就把SQL拿到测试环境用EXPLAIN分析。

这里要特别提醒一下,线上环境执行SHOW FULL PROCESSLIST如果连接数特别多、线程特别多时,本身也会产生一定开销,但相比直接杀进程,这个命令的还是非常温和的。另外,如果连不上MySQL(比如连接数打满),可以临时在配置文件里跳过权限验证重启一下,或者通过增加一个保留连接(max_connections+ 1)的方式进去,这个技巧后面会展开讲。

2.3 第三步:开启慢查询日志与performance_schema定位历史SQL

很多CPU告警是偶发性的,你被叫上线的时候,那条罪魁祸首SQL可能已经执行完了,SHOW FULL PROCESSLIST里看不到任何异常。这时候就需要靠慢查询日志和performance_schema来复盘。

快速开启全局慢查询日志的方法:

SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 1; SET GLOBAL log_output = 'TABLE';

long_query_time设置为1秒的意思是,所有执行时间超过1秒的SQL都会被记录下来。注意log_output设置为TABLE后,日志会写入mysql.slow_log表,排查起来比文件方便很多。查询慢SQL:

SELECT start_time, query_time, rows_examined, rows_sent, db, sql_text FROM mysql.slow_log ORDER BY start_time DESC LIMIT 50;

rows_examined是表示这条SQL实际扫描了多少行数据,rows_sent表示最终返回了多少行。这两个字段的比值非常关键,如果前者是数百万、后者只有几十,说明这条SQL在“大海捞针”,索引使用情况大概率有问题。

performance_schema是更底层的分析工具。比如要看哪类SQL消耗的CPU时间最多(MySQL 5.7+支持events_statements_summary_by_digest),可以执行:

SELECT SCHEMA_NAME, DIGEST_TEXT, COUNT_STAR, SUM_ROWS_EXAMINED, SUM_ROWS_SENT, SUM_TIMER_WAIT / 1000000000 AS total_sec FROM performance_schema.events_statements_summary_by_digest ORDER BY SUM_TIMER_WAIT DESC LIMIT 20;

这条查询可以直接列出最消耗时间的SQL模板,配合慢查询日志去查看对应的SQL明细。不过要注意,performance_schema有一定内存开销,如果线上实例本身资源已经吃紧,开启之前最好评估一下风险。更稳妥的办法是,确认开启performance_schema开关然后重启数据库,或者在压力可控的窗口期开启相关消费者。

2.4 第四步:用pt-query-digest分析慢查询日志

如果慢查询日志内容很多,人工一条条看会疯掉的。Percona Toolkit里的pt-query-digest是处理这个问题的神器,它可以对慢查询日志做聚合分析,把相同结构的SQL归为一类,统计出每类SQL的执行次数、平均耗时、总耗时、扫描行数等。

假设慢查询日志文件在/var/lib/mysql/slow.log,分析命令:

pt-query-digest /var/lib/mysql/slow.log > slow_analysis.txt

打开生成的slow_analysis.txt,重点看两个部分。

第一部分是“Profile”,它会把所有SQL按照总耗时降序排列,最前面的就是最大瓶颈。如果某类SQL的总耗时占比超过50%,基本可以锁定主要矛盾。

第二部分是每个SQL的详细统计,包括Rows examine(平均扫描行数)、Rows send(平均返回行数)、Query time distribution(执行时间分布)。如果一个SQL平均扫描100万行却只返回10行,那它的索引设计基本是无效的,优化空间巨大。

我习惯在定位到问题SQL后,顺手做一个复盘记录,记下当时的CPU使用率、扫描行数、执行时间,优化之后再进行一次对比。这种方式既方便自我总结,也能在向领导汇报时提供直观的数字支撑。

3. 实战案例拆解:SQL索引失效如何把CPU打爆

这一章用一个真实场景还原整个排查过程。为了便于理解,我对表结构和数据量做了一些简化,但排查思路和线上完全一致。

3.1 简化表结构与问题描述

假设有一张订单表t_order

CREATE TABLE `t_order` ( `id` bigint NOT NULL AUTO_INCREMENT, `order_no` varchar(64) NOT NULL, `user_id` int NOT NULL, `status` tinyint NOT NULL DEFAULT 0, `amount` decimal(10,2) NOT NULL, `created_at` datetime NOT NULL, PRIMARY KEY (`id`), KEY `idx_user_id` (`user_id`) ) ENGINE=InnoDB;

表里有大约800万行数据。某个工作日10点,业务方反馈后台订单查询页面打开非常慢,紧接着告警系统发来MySQL CPU持续95%以上的通知。

登录服务器先做观察:top中mysqld占CPU 600%+(8核机器),vmstatus非常高,wa一般,说明CPU是在拼命计算而非等待磁盘。进入MySQL执行SHOW FULL PROCESSLIST,发现有好几条类似下面的查询在跑,有些已经执行了30多秒:

SELECT order_no, user_id, amount, created_at FROM t_order WHERE DATE(created_at) = '2024-06-01' ORDER BY amount DESC LIMIT 50;

3.2 用EXPLAIN看清执行计划的真相

将这条SQL在测试环境执行EXPLAIN

EXPLAIN SELECT order_no, user_id, amount, created_at FROM t_order WHERE DATE(created_at) = '2024-06-01' ORDER BY amount DESC LIMIT 50;

结果让人一眼看出问题:

列名
typeALL
keyNULL
rows8000000
filtered100.00
ExtraUsing where; Using filesort

type = ALL代表全表扫描,key = NULL代表没有使用任何索引,rows = 8000000说明优化器预估需要读取全表800万行,Extra里的Using filesort表示需要对结果进行文件排序,也就是说在内存中或磁盘上做ORDER BY排序。

这条SQL有两个致命点。第一个致命点是WHERE DATE(created_at) = '2024-06-01',在created_at字段上套了DATE()函数,导致原本可能在这个字段上建立的索引完全失效。MySQL的优化器对于索引列上套了函数或表达式的查询条件,基本无法直接使用B+树索引进行范围匹配,只能退化为全表扫描,做逐行计算后筛选。第二个致命点是ORDER BY amount DESC,排序字段和查询条件字段不一致,即使WHERE条件命中了索引,排序还是要另外单独处理,当数据量巨大时,排序过程会产生临时文件,进一步消耗CPU和磁盘IO。

3.3 优化方案:先改SQL,再补索引

针对这条慢SQL,最优的解决思路分两步走。

第一步,SQL改写,避免在索引列上使用函数。既然created_at是datetime类型,如果业务上要查询某一天的数据,合理的写法应该是:

SELECT order_no, user_id, amount, created_at FROM t_order WHERE created_at >= '2024-06-01 00:00:00' AND created_at < '2024-06-02 00:00:00' ORDER BY amount DESC LIMIT 50;

改写后,优化器可以对created_at做范围查询,如果有合适的索引就能用上。但依然有一个问题,排序字段amount和范围查询字段created_at不一致,需要额外排序。

第二步,根据实际业务场景建立联合索引。如果这类的查询是高频的,可以创建一个(created_at, amount)的联合索引,让MySQL在通过时间范围过滤数据时,直接按amount顺序读取,省去filesort:

ALTER TABLE t_order ADD INDEX idx_created_at_amount (created_at, amount);

这里补充一个联合索引的底层原理。B+树的索引结构是按照索引列的顺序逐层排序的,(created_at, amount)这个索引会先按created_at排序,同一created_at下再按amount排序。因此,当WHERE条件限定了created_at的范围时,索引内部已经天然按照amount排好了序,优化器直接顺序扫描索引就能得到有序结果,不需要额外的排序步骤。

加完索引之后再次EXPLAIN,结果变为:

列名
typerange
keyidx_created_at_amount
rows12000
ExtraUsing index condition

从全表扫描800万行锐减为范围扫描1.2万行,filesort也消失了。这条SQL在优化前的执行时间大约是35秒,优化后只有0.05秒,CPU使用率肉眼可见地降了下来。

3.4 从一次优化引出参数调优

问题SQL处理完,CPU已经从95%降到了50%左右,但仍然偏高。继续观察发现,还有一些并发很高的简单查询在占用CPU,比如按照user_id查询这个用户最近的订单:

SELECT * FROM t_order WHERE user_id = 123456 ORDER BY id DESC LIMIT 20;

这个SQL本身走了idx_user_id索引,看着没啥问题。问题在于,SELECT *意味着查到索引后还要根据主键回表查询其他列的数据,一次查询回表20行,如果这个SQL的QPS很高,回表操作积累起来就是大量的随机读和CPU开销。

优化方式是覆盖索引,也就是把要查询的列都放到索引里:

ALTER TABLE t_order ADD INDEX idx_user_id_created_at (user_id, created_at, amount, order_no);

这样查询条件user_id、排序字段id(这里如果要完全覆盖也建议加id或者直接改成created_at排序)以及需要返回的列都包含在索引中,MySQL可以直接在索引中完成查询,完全不需要回表。Extra会出现Using index,这是代表“完全覆盖索引扫描”的标志,性能提升非常明显。

4. 连接数异常高涨导致的CPU耗尽

除了SQL本身的问题,另一种非常常见的情况是:单条SQL都不慢,但连接数高到吓人,CPU被线程调度消耗殆尽。

4.1 为什么连接数高会导致CPU高

MySQL每接收一个客户端连接,就要创建一个线程(或者从线程池中分配一个线程)来处理该连接上的请求。每个线程都有自己的栈空间,操作系统为了让所有线程公平地使用CPU,会不断进行上下文切换。

打个比方,一个餐厅只有一个厨师(CPU核心),正常情况下同时只有5桌客人点菜,厨师可以连贯地做菜。但如果一下子涌进来200桌客人,每桌都在催菜,厨师不得不每道菜只做五分钟就放下,去处理另一桌的需求,结果大部分时间浪费在“换菜谱”和“找锅铲”上,真正做菜的时间少得可怜。

数据库也是一样,当活跃连接数远超CPU核心数时,大量的CPU时间片消耗在线程切换上,真正执行SQL的时间被压缩,导致所有SQL都变慢,而变慢的SQL又进一步延长连接占用时间,形成恶性循环。

4.2 排查连接数问题的核心命令

确认是否存在连接数异常,主要看两个指标:当前连接数和最大连接数。执行:

SHOW GLOBAL STATUS LIKE 'Threads_connected'; SHOW GLOBAL STATUS LIKE 'Threads_running'; SHOW GLOBAL STATUS LIKE 'Max_used_connections'; SHOW VARIABLES LIKE 'max_connections';

Threads_connected代表当前连接数,Threads_running代表正在执行SQL的活跃连接数,Max_used_connections是历史最大连接数。如果Threads_connected长期接近max_connections,说明系统一直在连接数打满的边缘徘徊。如果Threads_running经常超过CPU核心数,说明有很多请求在同时执行,CPU必然过载。

如果连接数确实打满了,还需要继续确认是谁在发起这么多连接。可以从进程列表里看到:

SELECT user, host, db, command, count(*) FROM information_schema.processlist GROUP BY user, host, db, command ORDER BY count(*) DESC;

实际业务中,连接数暴涨最常见的原因有这么几类:应用侧的数据库连接池配置过大,比如Spring Boot的maximum-pool-size设置100甚至更高,每个应用实例再部署多个节点;慢查询导致应用等待时间变长,为保持并发吞吐,连接池不断创建新连接;某段时间流量突增,比如秒杀、活动预热;还有可能是负载均衡层面出现健康检查异常,导致流量集中路由到某一台数据库实例上。

4.3 紧急处置方法和长远的参数调整

如果连接数已经打满,正常的连接建立不进去了,可以尝试通过预留的额外连接强制登录。MySQL在max_connections之外预留了一个连接数,用于管理员在紧急情况下登录,这个参数叫super_read_only无关,真实的关键参数是max_connections+1,但默认情况下有个extra_max_connections的逻辑。方便的做法是提前在配置中设置:

[mysqld] max_connections = 500

同时通过skip-networking=0保持网络连接。实际遇过极端场景,连接数把端口占满、无法新建连接,这种情况下如果允许,可以在配置文件里调整max_connections为更大的值并重启数据库,但重启会影响所有现有连接,需要和业务方确认可维护时间窗口。

从长远来看,更需要做的是限制应用侧的连接池大小。以Java的HikariCP为例,标准的配置建议:

spring: datasource: hikari: maximum-pool-size: 20 minimum-idle: 5 connection-timeout: 30000

maximum-pool-size = 20对于大多数业务系统已经足够了。连接池大小并不是越大越好,理论上连接数最好和CPU核心数保持在一个合理的比例,太多连接反而造成调度浪费。一个经验公式是:核心业务数据库的活跃连接数控制在CPU核心数的2到4倍以内比较合适,读多写少场景适当放宽;全站统一入口的场景把连接数控制在CPU核心数2倍以内,避免大量连接在锁等待上互相消耗。

如果应用侧短时间无法调整,MySQL侧也有一些缓解手段。比如开启线程池(MariaDB或Percona分支支持,官方MySQL 8.0不支持线程池,但可以通过extra_port等方式做一些应急限制),或者使用连接复用和SQL限流工具来做保护。线上环境最好加上连接数监控以及活跃连接数监控,一旦超过阈值立即告警,避免等用户反馈才发现问题。

5. 配置层面的隐患:不只是SQL的锅

当你确认所有SQL都很快、连接数也不高,但CPU依然高的时候,问题很可能出在MySQL或操作系统的配置上。

5.1 InnoDB缓冲池与临时表配置的关系

InnoDB缓冲池(innodb_buffer_pool_size)是MySQL最重要的内存区域,它用来缓存数据页和索引页。如果这个值设置得太小,MySQL就必须频繁地把数据从磁盘读入内存,也会频繁把脏页刷到磁盘,这会产生大量IO操作。CPU在等待磁盘IO的空档期虽然处于等待状态,但调度和重试机制本身会带来额外开销。更关键的是,当内存不足时,排序和临时表操作会被迫落到磁盘,排序过程又回归到CPU的负担上。

一个比较通用的建议是,innodb_buffer_pool_size设置为服务器物理内存的60%到75%左右,具体取决于服务器是专机专用还是混合部署。如果是专机专用、服务器内存为64GB,可以考虑设置为40GB到48GB。设置过大会导致操作系统本身几乎没有内存可缓存文件,甚至触发swap,反而不如小一点稳。

临时表的配置同样值得关注。MySQL执行大批量GROUP BYORDER BYDISTINCT时,需要创建临时表。如果tmp_table_sizemax_heap_table_size都太小,临时表很快就从内存转为磁盘临时表(使用MyISAM或InnoDB on-disk临时表)。磁盘临时表的读写速度比内存慢几个数量级,而且写入临时表时数据是逐步累积的,每一行数据的写入都可能触发CPU参与缓冲区管理和索引维护。

查看临时表使用情况:

SHOW GLOBAL STATUS LIKE 'Created_tmp_disk_tables'; SHOW GLOBAL STATUS LIKE 'Created_tmp_tables';

如果Created_tmp_disk_tables / Created_tmp_tables的比例超过25%,说明内存临时表配置大概率不够,可以考虑适当调大tmp_table_sizemax_heap_table_size

5.2 注意操作系统层面的大页和多核调度问题

Linux操作系统层面也有两个容易踩的坑。

第一个坑是vm.swappiness设置过高,默认值是60。这个参数控制操作系统将内存页交换到swap的倾向,如果MySQL所在机器内存充足但swappiness过高,操作系统可能把一些不太活跃的MySQL内存页换到swap中,之后访问这些页时又发生缺页中断,导致性能骤降。DBA一般建议将其调整为10甚至0,并通过sysctl -w vm.swappiness=10临时生效,持久化需要写入/etc/sysctl.conf

第二个坑是NUMA(非统一内存访问架构)问题。在多路CPU的服务器上,每个CPU有自己的本地内存,MySQL线程可能被调度到远端CPU上访问内存,这会增加访问延迟。有些场景分配MySQL的CPU核数和内存节点不对齐,就会导致大量远端内存访问,CPU和内存都在高压力下。此时可以考虑关闭NUMA、或者用numactl --interleave=all让内存访问均匀分布,但要测试确认没有副作用后再上生产。

这些系统层的调优往往会在高并发场景下带来惊喜,但在业务量小的环境里不容易看出差异,所以经常被忽视。我个人建议在每次新服务器上线前就完成这些基础配置的初始化,而不是等到出事后才去排查。

6. 常见问题速查表和我的避坑经验

这一章把平时排查中常遇到的场景、原因和解决方法整理成表格,方便大家以后直接对照。每个问题后面我也会补一句自己踩过的坑或者总结的经验。

6.1 CPU排查常见问题速查表

现象特征常见原因首选排查命令解决方向
CPU持续高,进程列表中大量Sending dataSQL全表扫描/索引失效SHOW FULL PROCESSLIST; EXPLAIN优化SQL、加联合索引或覆盖索引
CPU周期性飙高定时任务、报表查询未走索引慢查询日志、events_statements_summary_by_digest错峰执行、拆分查询、优化SQL
连接数打满,CPU高但SQL不慢连接池配置过大、流量突增information_schema.processlist分组统计调整连接池,限制最大连接数,必要时重启
并发不高但是排序操作很多排序字段无索引,filesort严重EXPLAIN看Extra的Using filesort建立合适的联合索引
内存和CPU同时告警缓冲池设置过小、临时表落磁盘SHOW GLOBAL STATUS LIKE 'Created_tmp_disk_tables'调大buffer pool和tmp_table_size
磁盘等待高但CPU也让高慢SQL和业务形成恶性循环vmstat观察wa和us优先优化IO慢SQL,可能涉及SSD升级或拆分数据量
CPU高但mysqld占用不高其他进程干扰top检查其他进程排查对应进程,不要盲目优化数据库
CPU高伴随大量锁等待锁竞争、死锁引起事务堆积SHOW ENGINE INNODB STATUS; 锁监控优化事务大小、加索引覆盖锁范围、减少长事务

6.2 我踩过的一些坑

第一,别在CPU高的时候贸然重启数据库。重启看似能一秒清空所有状态,但重启后缓冲池被清空,需要预热,瞬间大量SQL全部走磁盘读取,CPU和IO压力不降反升。除非是连接数彻底打满无法登录、且没有任何其他方式恢复的绝境,否则不建议走这条回头路。

第二,别被一条“看起来很正常的SQL”骗了。有时候EXPLAIN结果显示key是主键、typeref,看着没什么问题,但实际执行时因为使用了OR条件、IN列表过长、或者ORDER BYWHERE字段不匹配,导致优化器选择了临时表和文件排序。这些都要在慢查询日志中结合rows_examined才能真正发现问题。

第三,SHOW FULL PROCESSLIST看到的SQL可能不是根源。比如你看到一个UPDATE在执行,看起来很慢,但真正原因可能是它之前长长的事务一直没提交,锁被占用,导致这个UPDATE卡在等待锁上。所以看到State里面有updatinglocked时,要结合information_schema.innodb_trx看事务状态。

第四,不要忽略自增主键频繁插入带来的页分裂开销。很多同学在优化时只关注SELECT,但写密集的批量插入、频繁删除操作也会导致索引页分裂、B+树结构调整,这个过程CPU消耗很大。排查的时候可以把SHOW GLOBAL STATUS LIKE 'Handler_write'Innodb_pages_written拉出来看看,判断是否有异常高的写放大。

6.3 最后的几条建议

说一下我自己的处理节奏。接到CPU告警后,我会先花两分钟回答三个问题:数据库是否还能登录?CPU是稳态高还是抖动高?有没有明显的时间关联?然后按照查进程、查SQL、查配置的顺序走,千万不要跳过操作系统层面的判断直接去看SQL,也不要看到一条慢SQL就急着kill。

补充一个实用小技巧:给MySQL配置一个“应急账号”,权限只包含登录和查询,用于CPU告警时快速进入数据库查看状态,避免使用主账号引发安全问题。还可以把常用的排查SQL写成SQL脚本或者Shell脚本,告警时一键执行,节省救火时间。

从实际经验来看,MySQL CPU使用率高的问题,百分之八十以上都是慢SQL导致的,剩下的百分之二十分布在连接数、配置、系统层面。只要把排查链路练熟了,形成了自己的“肌肉记忆”,下次再遇到告警就能从容很多。希望这篇分享能帮你少走一些我走过的弯路。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 23:01:46

VS Code插件与环境配置实战:从编辑器到高效开发工作流

刚接触 VS Code 的人&#xff0c;往往会被它的插件市场吓一跳——搜一个 "Chinese" 能出来几十个结果&#xff0c;配一个 C/C 环境能搜到一堆教程但照着做还是报错。作为从 Sublime 转过来、用了好几年 VS Code 的深度用户&#xff0c;我想把平时真正沉淀下来、每天都…

作者头像 李华
网站建设 2026/9/19 23:01:17

Spring Boot教学辅助平台:权限设计、Redis签到与部署实战

简介&#xff1a;基于SpringBoot的教学辅助平台设计与实现完整文档&#xff0c;面向高校计算机相关专业学生、JavaWeb初学者及需要完成类似课题的毕业设计者&#xff0c;提供从需求分析到系统实现的整体参考。包内共1个docx文件&#xff0c;大小1.36MB&#xff0c;即该课题的完…

作者头像 李华
网站建设 2026/9/19 22:59:29

QuickRecorder 轻量录屏完整上手指南:免虚拟声卡录系统声音

QuickRecorder 轻量录屏完整上手指南&#xff1a;免虚拟声卡录系统声音 【免费下载链接】QuickRecorder A lightweight screen recorder based on ScreenCapture Kit for macOS / 基于 ScreenCapture Kit 的轻量化多功能 macOS 录屏工具 项目地址: https://gitcode.com/GitHu…

作者头像 李华
网站建设 2026/9/19 22:58:17

系统分析师论文备考:拆解范文结构与构建素材库的实战方法

简介&#xff1a;面向全国计算机软考系统分析师论文备考者&#xff0c;这份范文PDF围绕“Java技术在因特网平台上的应用”展开&#xff0c;以通信信息服务平台为真实案例&#xff0c;完整呈现论文摘要、正文论述与结尾策略&#xff0c;适合需要快速熟悉论文结构、积累技术素材与…

作者头像 李华
网站建设 2026/9/19 22:56:53

Vibe Gaming 一人工作室微信小游戏开发实战:AI编程与变现全链路

一人工作室做微信小游戏&#xff0c;最真实的体感是&#xff1a;你既是策划、美术、程序&#xff0c;也是测试和运营。过去两年我陆续用 AI 编程工具配合微信开发者工具&#xff0c;独立完成了三款小游戏的上线与迭代&#xff0c;踩过的坑从环境配置到排行榜接入、从广告变现到…

作者头像 李华