返回文章列表

文章

分析数据库CPU、I/O等占用过高

从查询层面、架构/设计层面、系统/环境层面和外部/维护任务四个维度,详细剖析常见原因、排查思路与优化建议

目录
  1. 在生产环境中,数据库出现 CPU 利用率飙升 或 I/O 占用过高 往往意味着某些操作或配置不当,导致数据库无法高效地执行读写请求。下面从查询层面、架构/设计层面、系统/环境层面和外部/维护任务四个维度,详细剖析常见原因、排查思路与优化建议。
  2. 一、查询层面
  3. 1. 复杂/低效 SQL 导致 CPU 飙升
  4. 2. 频繁的短事务/大事务导致锁竞争
  5. 3. 索引失效或未命中索引
  6. 二、架构/设计层面
  7. 1. 表结构与分区设计不合理
  8. 2. 并发连接数/连接池配置过大
  9. 3. 事务隔离级别与锁争用
  10. 三、系统/环境层面
  11. 1. 磁盘 I/O 瓶颈
  12. 2. 日志/二进制日志(Binlog)相关 I/O
  13. 3. 操作系统层面
  14. 四、外部/维护任务因素
  15. 1. 统计/索引重建/数据导出导入
  16. 2. 自动化脚本与监控任务
  17. 3. 复制延迟与从库压力
  18. 五、综合诊断思路
  19. 六、常见优化建议
  20. 1. 针对查询层面
  21. 2. 针对事务与并发
  22. 3. 针对硬件与配置
  23. 4. 针对维护与监控
  24. 七、小结
  25. 📎 参考文章

在生产环境中,数据库出现 CPU 利用率飙升I/O 占用过高 往往意味着某些操作或配置不当,导致数据库无法高效地执行读写请求。下面从查询层面架构/设计层面系统/环境层面外部/维护任务四个维度,详细剖析常见原因、排查思路与优化建议。#

一、查询层面#

1. 复杂/低效 SQL 导致 CPU 飙升#

  • 全表扫描(Full Table Scan)
    • 当某条查询缺少合适的索引时,数据库不得不遍历整张表的每一行来定位符合条件的数据。对于行数上百万的表,这种操作会极大消耗 CPU,且伴随着大量数据从磁盘读入内存(I/O)。
    • 示例

-- 假设 orders 表有 5000 万行数据,但没有在 customer_id 列上建索引 SELECT * FROM orders WHERE customer_id = 12345; ``` 由于没有索引,数据库只能做全表扫描,CPU 用于比较每一行的 customer_id,I/O 也要将大部分页读入缓冲池(Buffer Pool)。

  • 子查询/嵌套查询不当
    • 嵌套子查询如果无法被优化器下推,可能会在外层循环里反复执行内层查询,造成“N+1 次查询”,每次都触发新的扫描或小范围查找,累积起来对 CPU 和 I/O 都是重灾区。
    • 示例

-- 错误示例:对于每条用户,重新执行一次子查询 SELECT u.id, u.name, (SELECT COUNT(*) FROM orders o WHERE o.user_id = u.id AND o.status = 'PAID') FROM users u WHERE u.active = 1; ``` 如果活跃用户上百万,这条 SQL 会导致成百上千次的子查询,CPU 用于重复计算,I/O 用于重复读取同一张 orders 表的数据块。

  • 大量聚合/排序/Group By
    • 在单个查询里对海量数据做 GROUP BYORDER BY,尤其没有合适索引或需要对整个表做排序时,会将大量数据从缓存里拉出,做排序算法(如快速排序、堆排序),CPU 开销陡增,同时也对磁盘 I/O 有很强的依赖。
    • 示例

SELECT product_id, SUM(amount) AS total_sales FROM order_items WHERE order_date BETWEEN '2024-01-01' AND '2024-12-31' GROUP BY product_id ORDER BY total_sales DESC LIMIT 100; ``` 如果 order_items 表有千万级别的行数,上面没有对 order_date 建合适分区或索引,就需要扫描整表并排序整个结果集,CPU 和 I/O 都是瓶颈。

  • 函数/表达式索引无法利用
    • 使用带有函数、表达式的 WHERE 条件(如 WHERE YEAR(created_at)=2025)时,索引往往无法命中,需要做额外的计算并全表扫描。
    • 示例

SELECT * FROM invoices WHERE YEAR(invoice_date) = 2025; ``` 改进:可在表里新建一个虚拟列(Generated Column)或额外存储“年度”字段,然后在该字段上加索引,以避免全表扫描。

2. 频繁的短事务/大事务导致锁竞争#

  • 大量短事务(Short-lived Transactions)频繁提交
    • 对于 MySQL InnoDB 引擎,每次事务提交都要更新 Redo Log、Undo Log 并做日志刷新(fsync),当短事务非常多时,日志写和刷新操作会带来强烈 I/O 压力,同时 CPU 也要调度锁和事务状态。
    • 示例

START TRANSACTION; INSERT INTO logs (user_id, action) VALUES (123, 'VIEW'); COMMIT; ``` 如果短事务并发非常高(千级以上),Redo Log 持续写满、Undo Log 频繁生成,CPU 处理事务管理的开销和 I/O 盘刷压力都会升高。

  • 长事务/大事务(Long/Big Transactions)
    • 一次性更新或插入成千万行数据,会导致事务持有锁时间过长:
      • 在写操作过程中,旧版本行被标记为删除状态,需要保留 Undo 信息,影响并发读取。
      • 直到事务提交之前,Undo Log 无法清理,导致 Buffer Pool 和 Undo 区占用膨胀。
      • 一旦提交,还需要清理行锁、写 Binlog(如在主从复制模式下),瞬时 I/O 高峰非常明显。
    • 示例

START TRANSACTION; -- 更新数百万行 UPDATE users SET status = 'INACTIVE' WHERE last_login < '2023-01-01'; COMMIT; ``` 大事务会让 InnoDB Buffer Pool 中同时保留大量锁信息、Undo Log、Redo Log,加剧 I/O 写入并造成 CPU 长时间处理锁冲突。

3. 索引失效或未命中索引#

  • 索引列上使用不当操作
    • 在 WHERE 条件里对索引列做数学/字符串函数运算、加隐式类型转换,都会导致优化器无法使用索引、触发全表扫描。
    • 示例

-- 避免像下面这样写: SELECT * FROM products WHERE CONCAT(code_prefix, code_suffix) = 'XYZ123';

	```
	改为在表里存储拼接好的完整编码 `full_code` 并对其加索引,然后直接 `WHERE full_code = 'XYZ123'`。
  • 过多冗余/重复/不合理索引
    • 每个索引都需要额外的存储空间,并增加写操作(INSERT/UPDATE/DELETE)的开销。当表写入频繁时,索引越多,所需维护也越多,I/O 写和 CPU 比较值就会更高。
    • 建议:
      1. 定期审计索引使用情况(如 MySQL 可查看 sys.schema_unused_indexes
      2. 删除长期不命中或重复性高的索引
      3. 合理利用联合索引,避免单列索引泛滥

二、架构/设计层面#

1. 表结构与分区设计不合理#

  • 单表行数过多且未做分区/分表
    • 当单表数据量达到亿级、几十亿级别时,任何全表扫描、排序、分组都成为不可承受之重,I/O 需要读取海量页,CPU 需要对每行进行计算过滤。
    • 解决方案:
      1. 垂直分表:将业务相对独立的列拆分到不同表中,减少单表宽度;
      2. 水平分表/分库:按某个字段(如时间、用户 ID)将表拆成多个子表或多个数据库实例,降低单表行数。
      3. 分区表(Partitioning):MySQL/InnoDB 支持按范围、列表、Hash、键值分区,能让查询做分区裁剪(Partition Pruning),减少扫描范围。
  • 不当的冗余字段或过度范式化
    • 过度范式化会导致频繁的表连接(JOIN)操作。假设业务需要大量关联查询,JOIN 两、三张表后才能获取完整信息,每个 JOIN 会读入多张表的页并对齐匹配,CPU 需要做多次行比较。
    • 适当的反范式化(Denormalization):
      • 将常用查询涉及的字段合并到一张表,减少 JOIN 次数
      • 利用触发器或应用层在写时维护冗余字段,换来读时更少的 CPU 与 I/O 开销

2. 并发连接数/连接池配置过大#

  • 过高的最大连接数(max_connections)
    • 如果将 MySQL 的 max_connections 设置过高(如上万),当大量并发客户端同时连接时,数据库需要为每个连接分配会话结构、缓冲区(如每连接的 sort_buffer_size、join_buffer_size),导致内存使用猛增。
    • 如果这些连接都在不断执行查询/写入,会带来更严重的 I/O 与 CPU 压力。
    • 建议:根据服务器硬件和业务并发峰值合理设置 max_connections,在应用层使用连接池(如 HikariCP)来复用连接,而不是随意增加连接数。
  • 连接池中空闲连接过多仍然保持活跃
    • 当应用侧连接池保留大量空闲连接时,数据库端依然会分配资源(线程、内存缓冲),即使这些连接并不活跃,也会消耗系统资源。
    • 优化方法:
      1. 在连接池配置中设置合理的 minimumIdle(最小空闲连接数)和 maxLifetime(最大存活时间),让空闲连接能够及时关闭;
      2. 使用 testOnBorrow/testWhileIdle 保证连接有效,同时不必长期保留无用连接。

3. 事务隔离级别与锁争用#

  • 高隔离级别(如 Serializable)导致写锁频繁
    • 在 Serializable 模式下,任何读写操作都需要加排他锁或间隙锁(Gap Lock),极易与其他事务发生冲突,导致大量事务等待锁,最终产生写放大和死锁,CPU 要不断处理锁表、回滚、重试逻辑,I/O 则会因频繁写入和回滚日志而占用带宽。
    • 如果业务对一致性要求没有那么苛刻,可以考虑将隔离级别降为 Read Committed 或 Repeatable Read,减少锁争用。
  • 长事务持有行锁/表锁
    • 一旦某个大事务对表执行了范围更新(UPDATE ... WHERE ... 范围条件),会持有大量行锁(或页锁、锁的意向信息),其他事务在同一范围内的写操作只能等待锁释放。此时数据库会积累大量等待锁的事务,CPU 则用于调度锁等待队列,I/O 用于写入 Undo Log / Redo Log。
    • 限制长事务的存活时间:
      1. 将批量更新拆分成多次小批量执行
      2. 使用悲观锁/乐观锁机制,避免持锁时间过长
      3. 对于统计场景,考虑用 MVCC 快照读而非加锁读

三、系统/环境层面#

1. 磁盘 I/O 瓶颈#

  • 磁盘类型或 RAID 配置不当
    • 机械硬盘(HDD)在随机读写性能上远不如 SSD。如果数据库所在磁盘是 HDD,且没有做 RAID 0/10 提高随机 I/O 性能,面对大量并发小事务的读写,I/O 延迟会很高,进而让 CPU 等待上下文切换和 I/O 完成,从而出现高 CPU+I/O 等待场景。
    • SSD/NVMe 在高并发写入和随机读取场景下表现更优;若使用 SAN 存储,也要确保网络带宽和存储协议(iSCSI、Fibre Channel)不会成为瓶颈。
  • 磁盘队列(I/O Queue)过深或抖动
    • 当大量读写请求同时到来,OS 会把 I/O 请求排队到各个块设备(Block Device)。如果队列深度过大(例如 cat /sys/block/sda/queue/nr_requests 设置过小),会导致吞吐下降;相反,如果队列过长,也会带来高延迟。
    • 建议:
      1. 监控 iostat -x 1 中的 awaitutilsvctm 等指标,判断 I/O 瓶颈。
      2. 优化 Linux I/O 调度器(Elevator),例如在 SSD 上选择 noopdeadline 而非默认的 cfq
  • Buffer Pool 缓存未命中
    • 以 MySQL InnoDB 为例,当 Buffer Pool(InnoDB 缓存页)设置过小,热点数据无法全部缓存在内存,导致大量“冷数据”需要从磁盘读取。每次查询都触发磁盘读(I/O),同时 CPU 也要等待并解压数据。
    • 优化:
      1. 根据可用内存合理设置 innodb_buffer_pool_size,通常为服务器总内存的 60%~70%。
      2. 定期检查 Buffer Pool 命中率(SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_reads';Innodb_buffer_pool_read_requests)并进行调整。

2. 日志/二进制日志(Binlog)相关 I/O#

  • Binlog 同步模式导致阻塞
    • 在主从复制模式下,MySQL 的 Binlog 写入分为 sync_binlog 的参数控制:
      • sync_binlog=1 表示每条事务都强制刷写到磁盘,保证主从同步数据安全,但 I/O 消耗极大;
      • sync_binlog=0 或较大数值则会缓冲多次写入再刷盘,降低 I/O 压力,但一旦崩溃可能丢失最后几条事务。
    • 建议:在对安全性要求较高的业务,适当配置 sync_binlog=1;但要配合更高效的存储设备(SSD/RAID)。否则会导致持续的 I/O 磁盘刷盘(fsync),I/O 利用率上升,CPU 也会因调用内核 fsync 系统调用而增加上下文切换。
  • Redo Log/Undo Log 写入压力
    • InnoDB 写操作要先写入 Redo Log,再提交事务;大型事务会生成大量 Undo Log(回滚信息)。如果 innodb_log_file_size 设置过小,Redo Log 频繁循环覆盖,增大 I/O 写负担;如果 innodb_log_buffer_size 设置过小,也会导致事务提交时频繁将日志从缓冲刷入磁盘。
    • 优化:
      1. 根据写入量及事务大小合理调整 innodb_log_file_size(通常为 512MB~1GB 以上);
      2. 增大 innodb_log_buffer_size,避免短事务频繁磁盘落盘;
      3. 定期监控 Redo Log、Undo Log 的写入、等待情况,如果发现大量等待(Innodb_log_waits)指标异常,则需要增大日志参数。

3. 操作系统层面#

  • 系统级别备份/压缩引发 I/O 峰值
    • 如果在业务高峰期执行整库备份(如使用 mysqldumpxtrabackupmysqlhotcopy),会对磁盘造成大量顺序读/写,对 I/O 带宽占用极大,间接影响数据库正常的读写。
    • 建议:将物理备份或导出任务安排在业务低谷期,使用低优先级 I/O(ionice)等手段减少对正常业务的冲击。
  • 文件系统/RAID 重建、硬件检查
    • 在 RAID 阵列发生故障重建或文件系统做完整性检查(fsck)时,会持续进行大量读写操作,导致数据库所在分区 I/O 队列饱和。此时 CPU 也会收发磁盘中断、调度驱动程序。
    • 如果 RAID 磁盘预警,也可能触发后台重构操作,持续消耗 I/O,并产生延迟。
    • 监控 dmesg/var/log/kern.log 中是否有 RAID 重建提示或硬盘 SMART 告警,必要时及时更换故障硬盘。

四、外部/维护任务因素#

1. 统计/索引重建/数据导出导入#

  • 大批量数据导出(Export)或导入(Import)
    • 使用 SELECT ... INTO OUTFILELOAD DATA INFILEmysqldump 导入导出数百万行、上千万行时,会产生大量磁盘读写和网络传输消耗,I/O 与 CPU 都会短时间内飙升。
    • 建议:
      1. 对导入导出操作使用分批处理或分表并行操作;
      2. 当导入 CSV、文本文件时,使用 LOAD DATA INFILE 而非逐行 INSERT,可以显著降低 CPU 与 I/O 压力。
      3. 导出时可先用 SELECT ... LIMIT x OFFSET y 分批导出,或者使用 mysqldump --single-transaction 保持 InnoDB 一致性,同时减少对线上写入的锁争用。
  • 索引重建(Rebuild Index)与优化表(OPTIMIZE TABLE)
    • 当执行 ALTER TABLE ... ADD INDEXCREATE INDEXOPTIMIZE TABLE 时,数据库会对整个表做读写重写:
      1. 全量读取表数据到临时表
      2. 按照索引顺序写入新表
      3. 交换表文件
    • 过程会对磁盘 I/O 造成极大压力,CPU 也会参与排序、压缩页等作业。
    • 建议:
      1. 非高峰期执行这些操作,必要时使用在线 DDL(如 MySQL 5.6+ 的 InnoDB Online DDL)来减少阻塞;
      2. 对于大表,可先在副本库或从库上完成索引构建,再做主从切换,避免影响主库性能。

2. 自动化脚本与监控任务#

  • 定时统计/报表脚本(CRON)
    • 一些定时任务需要做大范围数据扫描、统计并写入结果,如果不控制执行时间或分批逻辑,就会在预定时间点集中占用数据库 I/O 与 CPU。
    • 建议:
      1. 将统计任务分散到不同时间段执行(shuffle cron time);
      2. 对统计任务加限速或分页,避免一次性大查询;
      3. 在统计时适当使用预聚合表、物化视图,减少每次全表扫描次数。
  • 监控巡检/健康检查
    • 一些监控工具会定期执行类似 SELECT COUNT(*) FROM tableSHOW TABLE STATUSSELECT metric FROM information_schema 等操作,如果数据库表过大或监控频次过高,也会给 I/O 带来不小压力。
    • 建议:
      1. 调整监控频率,非核心指标可以每 5 分钟或 10 分钟采集一次;
      2. 使用轻量级的监控接口(如 INFORMATION_SCHEMA 小表、performance_schema),避免每次扫描大量业务表。

3. 复制延迟与从库压力#

  • 主从复制压力导致 I/O 争用
    • 如果从库用来做报表或开发环境,而你在从库上执行大批量查询,可能会影响从库的 I/O 资源,从而拖慢 Binlog Relay Log 的应用速度,导致复制延迟。主库此时也会因为从库拉取延迟造成更多 Undo/Redo 保留,I/O 和 CPU 都会被拖累。
    • 优化:
      1. 对从库做只读访问,并监控 Seconds_Behind_Master,发现延迟需降低对从库的负载;
      2. 为从库分配独立磁盘(或者使用更高性能存储),避免与主库冲突;
      3. 或者把报表/分析任务搬到专门的分析型数据库(如 Pentaho、ClickHouse、Elasticsearch),减少对生产库的压力。

五、综合诊断思路#

当监控发现数据库 CPU 持续在 80%~100% 或 I/O 等待(iowait)偏高时,可按以下思路系统性排查:

  1. 基础监控指标
    • CPU 利用率tophtop 看数据库进程(如 mysqld)的 %CPU;如果多个线程都跑满,优先关注是并发查询还是后台任务。
    • I/O 等待iostat -x 1 中查看 %iowait,以及每秒读写请求(r/s、w/s)与吞吐量(rMB/s、wMB/s);如果 %iowait > 50% 且磁盘吞吐高,说明磁盘是瓶颈。
    • Buffer Pool 命中率:MySQL InnoDB 可监控 Innodb_buffer_pool_read_requests vs. Innodb_buffer_pool_reads,如果命中率低于 95%,说明缓存不足,需要调大 innodb_buffer_pool_size
    • 慢查询日志:开启 long_query_time(如 1s),记录慢查询,并定期分析(pt-query-digestMySQL Enterprise Monitor)。
    • 事务与锁等待SHOW ENGINE INNODB STATUS 观察当前锁等待情况,如果有大量的 “Waiting for lock” 或 “Semaphore waits”,就要针对锁争用做优化。
  2. 定位高耗 SQL
    • 使用 SHOW PROCESSLIST 查看当前正在执行的语句、执行时间、状态(如 “Copying to tmp table”、“Sorting result”)。
    • 对可疑 SQL 做 EXPLAIN 分析,看是否走索引、是否触发了 filesort、temporary。
    • 定期对慢查询日志做归档与聚合分析,找出 TOP 10 最耗时或最频繁扫描的语句。
  3. 检查索引与表状态
    • SHOW INDEX FROM table_name 检查该表是否缺少常用查询条件的索引。
    • 利用 ANALYZE TABLE 更新统计信息,让优化器能够选择最优执行计划。
    • 对于 InnoDB 表,定期执行 OPTIMIZE TABLE(离线或在线)回收碎片,尤其在大量 DELETE/UPDATE 后,表碎片会影响 I/O 与扫描性能。
  4. 监控系统层面的 I/O/MEM/CPU
    • 在 Linux 层面同时监控 vmstat 1sar -u 1iostat -dx 1 等,准确判断是 CPU 瓶颈还是 I/O 瓶颈。
    • 观察磁盘队列长度(avgqu-sz)、吞吐延迟(await)、利用率(%util),如果 %util > 80%await 超过 20ms,就说明磁盘 I/O 可能成为瓶颈,需要考虑升级存储或优化 I/O 调度。
    • 监控内存使用和 swap 使用,避免 Buffer Pool 溢出到 Swap,导致 I/O 延迟更高。
  5. 评估硬件与配置
    • 根据业务负载峰值,评估当前 CPU 核数、内存大小、磁盘类型是否能满足需求,尤其在数据量大幅增长后,要及时扩容。
    • 检查 MySQL 配置文件(my.cnf)中:
      • innodb_buffer_pool_sizeinnodb_log_file_sizeinnodb_log_buffer_size 是否合理;
      • max_connectionsthread_cache_sizetable_open_cache 是否设置过大或过小;
      • 是否开启了慢查询日志、Performance Schema,以便持续监控。

六、常见优化建议#

1. 针对查询层面#

  1. 补齐或拆分索引
    • 根据慢查询分析结果,对 WHERE 条件常用字段、JOIN 条件字段加联合索引。
    • 对于覆盖索引查询(覆盖索引能够包含 SELECT 列),避免回表,减少 I/O。
  2. 避免函数/表达式在索引列上
    • 如果需要对日期按天、月或年分组,建议提前建“年月日”辅助字段,并给辅助字段建索引;
    • 或者在查询时用范围条件替代函数,如:

-- 不要写:WHERE DATE(order_time) = '2025-05-01' -- 改为: WHERE order_time >= '2025-05-01 00:00:00' AND order_time < '2025-05-02 00:00:00';

	```

3. 拆分大查询、分页优化 - 对于分页查询,避免 LIMIT offset, size 出现的大 OFFSET。可以使用 “基于索引游标” 的分页(比如 WHERE id > 上一次最大 id LIMIT size)。 - 对于聚合或统计类查询,可提前做预聚合表或物化视图,减少对原始大表的计算压力。

2. 针对事务与并发#

  1. 缩短事务粒度
    • 优化业务逻辑,让事务尽可能只包含必要的更新/写操作;耗时长的计算、校验尽量放到事务外、异步或批量执行。
  2. 合理选择隔离级别
    • 如果对 脏读(Dirty Read) 可以容忍,选用 Read Uncommitted 或 Read Committed;如果只关心“读到其他事务正在提交之前的数据”,使用 Repeatable Read 即可,无需上升到 Serializable。
  3. 使用乐观锁或应用层控制
    • 对于更新冲突频繁的热点数据,可在应用层用版本号或时间戳控制更新,避免数据库行锁争用。

3. 针对硬件与配置#

  1. 提高 Buffer Pool 容量
    • 让热点数据都能缓存在内存里,降低磁盘随机读次数。
    • 对于 SSD,Buffer Pool Size 可设为物理内存的 70%~80%;对于 HDD,建议为 50%~60%。
  2. 优化 Redo Log/Undo Log 参数
    • innodb_log_file_size 设大一些(如 1G~4G),减少 Redo Log 循环频率;
    • innodb_log_buffer_size 设到 64M~256M,避免短事务频繁落盘;
    • innodb_flush_log_at_trx_commit:如果对数据丢失要求不严格,可设为 2,让 Redo Log 每秒一次刷盘,降低 fsync 次数。
  3. 调整磁盘 I/O 调度器
    • 在 SSD 上使用 noopdeadline 调度器,在 HDD 上可考虑 deadline 以减少磁盘寻道延迟;
    • 如在 Linux 上可执行:

echo deadline > /sys/block/sda/queue/scheduler

	```

4. 对数据库实例做只读分离或水平扩展 - 对于读多写少的业务,可以通过主从复制将读请求分散到多个从库,减轻主库压力; - 对于写入量急剧增大时,考虑分库分表或分布式数据库(如 TiDB、CockroachDB)来做水平扩展。

4. 针对维护与监控#

  1. 做好慢查询/热点表监控
    • 定期生成慢查询报告(使用 pt-query-digest、Percona Toolkit);
    • 针对频繁读写的热点表(如用户表、订单表),考虑冷热分离、分区表等机制。
  2. 维护任务避开高峰期
    • 将备份、索引重建、统计脚本等高消耗操作安排在业务低谷时段;
    • 对备份任务加 I/O 限速(ionice)或使用增量备份,减少对正常业务的干扰。
  3. 手工/自动清理过期数据
    • 对历史数据做归档或分区裁剪,将过旧、访问极少的冷数据迁移到归档库,保持主表尽量小。
    • 定期清理旧日志、过期缓存、临时表,避免表膨胀影响查询性能。

七、小结#

  • 核心原因:数据库出现 CPU 占用过高 多来源于“复杂/低效查询”、“索引失效”、“频繁日志写入”等需要大量计算或磁盘读取的操作;出现 I/O 占用过高 则常因“Buffer Pool 不足”、“Redo Log/Undo Log 频繁落盘”、“大事务/批量操作”等导致磁盘读写压力骤增。
  • 综合定位思路
    1. 先看系统层面(iostatvmstattop)是 CPU 饱和还是 I/O 阻塞;
    2. 再看数据库层面(慢查询日志、SHOW PROCESSLISTSHOW ENGINE INNODB STATUSEXPLAIN)定位高耗 SQL;
    3. 最后结合架构/分区/硬件配置做全面优化。
  • 持续优化原则
    1. 查询先行优化,尽量做到“有索引就不扫表、有缓存就少读盘”;
    2. 事务与并发控制,让长事务、锁等待尽可能短;
    3. 硬件与配置匹配,保证 Buffer Pool、Redo Log、IOD 调度等都与业务负载相匹配;
    4. 监控与告警,定期审计索引使用、慢查询、复制延迟、磁盘健康,及时触发扩容或重构。 只要按照上述思路逐步排查、修复,通常都能有效降低数据库 CPUI/O 的消耗,确保整体服务稳定、响应及时。

📎 参考文章#