文章
分析数据库CPU、I/O等占用过高
从查询层面、架构/设计层面、系统/环境层面和外部/维护任务四个维度,详细剖析常见原因、排查思路与优化建议
目录
- 在生产环境中,数据库出现 CPU 利用率飙升 或 I/O 占用过高 往往意味着某些操作或配置不当,导致数据库无法高效地执行读写请求。下面从查询层面、架构/设计层面、系统/环境层面和外部/维护任务四个维度,详细剖析常见原因、排查思路与优化建议。
- 一、查询层面
- 1. 复杂/低效 SQL 导致 CPU 飙升
- 2. 频繁的短事务/大事务导致锁竞争
- 3. 索引失效或未命中索引
- 二、架构/设计层面
- 1. 表结构与分区设计不合理
- 2. 并发连接数/连接池配置过大
- 3. 事务隔离级别与锁争用
- 三、系统/环境层面
- 1. 磁盘 I/O 瓶颈
- 2. 日志/二进制日志(Binlog)相关 I/O
- 3. 操作系统层面
- 四、外部/维护任务因素
- 1. 统计/索引重建/数据导出导入
- 2. 自动化脚本与监控任务
- 3. 复制延迟与从库压力
- 五、综合诊断思路
- 六、常见优化建议
- 1. 针对查询层面
- 2. 针对事务与并发
- 3. 针对硬件与配置
- 4. 针对维护与监控
- 七、小结
- 📎 参考文章
在生产环境中,数据库出现 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 BY、ORDER 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)时,索引往往无法命中,需要做额外的计算并全表扫描。 - 示例:
- 使用带有函数、表达式的 WHERE 条件(如
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 比较值就会更高。
- 建议:
- 定期审计索引使用情况(如 MySQL 可查看
sys.schema_unused_indexes) - 删除长期不命中或重复性高的索引
- 合理利用联合索引,避免单列索引泛滥
- 定期审计索引使用情况(如 MySQL 可查看
二、架构/设计层面#
1. 表结构与分区设计不合理#
- 单表行数过多且未做分区/分表
- 当单表数据量达到亿级、几十亿级别时,任何全表扫描、排序、分组都成为不可承受之重,I/O 需要读取海量页,CPU 需要对每行进行计算过滤。
- 解决方案:
- 垂直分表:将业务相对独立的列拆分到不同表中,减少单表宽度;
- 水平分表/分库:按某个字段(如时间、用户 ID)将表拆成多个子表或多个数据库实例,降低单表行数。
- 分区表(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)来复用连接,而不是随意增加连接数。
- 如果将 MySQL 的
- 连接池中空闲连接过多仍然保持活跃
- 当应用侧连接池保留大量空闲连接时,数据库端依然会分配资源(线程、内存缓冲),即使这些连接并不活跃,也会消耗系统资源。
- 优化方法:
- 在连接池配置中设置合理的
minimumIdle(最小空闲连接数)和maxLifetime(最大存活时间),让空闲连接能够及时关闭; - 使用
testOnBorrow/testWhileIdle保证连接有效,同时不必长期保留无用连接。
- 在连接池配置中设置合理的
3. 事务隔离级别与锁争用#
- 高隔离级别(如 Serializable)导致写锁频繁
- 在 Serializable 模式下,任何读写操作都需要加排他锁或间隙锁(Gap Lock),极易与其他事务发生冲突,导致大量事务等待锁,最终产生写放大和死锁,CPU 要不断处理锁表、回滚、重试逻辑,I/O 则会因频繁写入和回滚日志而占用带宽。
- 如果业务对一致性要求没有那么苛刻,可以考虑将隔离级别降为 Read Committed 或 Repeatable Read,减少锁争用。
- 长事务持有行锁/表锁
- 一旦某个大事务对表执行了范围更新(
UPDATE ... WHERE ...范围条件),会持有大量行锁(或页锁、锁的意向信息),其他事务在同一范围内的写操作只能等待锁释放。此时数据库会积累大量等待锁的事务,CPU 则用于调度锁等待队列,I/O 用于写入 Undo Log / Redo Log。 - 限制长事务的存活时间:
- 将批量更新拆分成多次小批量执行
- 使用悲观锁/乐观锁机制,避免持锁时间过长
- 对于统计场景,考虑用 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设置过小),会导致吞吐下降;相反,如果队列过长,也会带来高延迟。 - 建议:
- 监控
iostat -x 1中的await、util、svctm等指标,判断 I/O 瓶颈。 - 优化 Linux I/O 调度器(Elevator),例如在 SSD 上选择
noop或deadline而非默认的cfq。
- 监控
- 当大量读写请求同时到来,OS 会把 I/O 请求排队到各个块设备(Block Device)。如果队列深度过大(例如
- Buffer Pool 缓存未命中
- 以 MySQL InnoDB 为例,当 Buffer Pool(InnoDB 缓存页)设置过小,热点数据无法全部缓存在内存,导致大量“冷数据”需要从磁盘读取。每次查询都触发磁盘读(I/O),同时 CPU 也要等待并解压数据。
- 优化:
- 根据可用内存合理设置
innodb_buffer_pool_size,通常为服务器总内存的 60%~70%。 - 定期检查 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 系统调用而增加上下文切换。
- 在主从复制模式下,MySQL 的 Binlog 写入分为
- Redo Log/Undo Log 写入压力
- InnoDB 写操作要先写入 Redo Log,再提交事务;大型事务会生成大量 Undo Log(回滚信息)。如果
innodb_log_file_size设置过小,Redo Log 频繁循环覆盖,增大 I/O 写负担;如果innodb_log_buffer_size设置过小,也会导致事务提交时频繁将日志从缓冲刷入磁盘。 - 优化:
- 根据写入量及事务大小合理调整
innodb_log_file_size(通常为 512MB~1GB 以上); - 增大
innodb_log_buffer_size,避免短事务频繁磁盘落盘; - 定期监控 Redo Log、Undo Log 的写入、等待情况,如果发现大量等待(
Innodb_log_waits)指标异常,则需要增大日志参数。
- 根据写入量及事务大小合理调整
- InnoDB 写操作要先写入 Redo Log,再提交事务;大型事务会生成大量 Undo Log(回滚信息)。如果
3. 操作系统层面#
- 系统级别备份/压缩引发 I/O 峰值
- 如果在业务高峰期执行整库备份(如使用
mysqldump、xtrabackup、mysqlhotcopy),会对磁盘造成大量顺序读/写,对 I/O 带宽占用极大,间接影响数据库正常的读写。 - 建议:将物理备份或导出任务安排在业务低谷期,使用低优先级 I/O(
ionice)等手段减少对正常业务的冲击。
- 如果在业务高峰期执行整库备份(如使用
- 文件系统/RAID 重建、硬件检查
- 在 RAID 阵列发生故障重建或文件系统做完整性检查(
fsck)时,会持续进行大量读写操作,导致数据库所在分区 I/O 队列饱和。此时 CPU 也会收发磁盘中断、调度驱动程序。 - 如果 RAID 磁盘预警,也可能触发后台重构操作,持续消耗 I/O,并产生延迟。
- 监控
dmesg、/var/log/kern.log中是否有 RAID 重建提示或硬盘 SMART 告警,必要时及时更换故障硬盘。
- 在 RAID 阵列发生故障重建或文件系统做完整性检查(
四、外部/维护任务因素#
1. 统计/索引重建/数据导出导入#
- 大批量数据导出(Export)或导入(Import)
- 使用
SELECT ... INTO OUTFILE、LOAD DATA INFILE、mysqldump导入导出数百万行、上千万行时,会产生大量磁盘读写和网络传输消耗,I/O 与 CPU 都会短时间内飙升。 - 建议:
- 对导入导出操作使用分批处理或分表并行操作;
- 当导入 CSV、文本文件时,使用
LOAD DATA INFILE而非逐行 INSERT,可以显著降低 CPU 与 I/O 压力。 - 导出时可先用
SELECT ... LIMIT x OFFSET y分批导出,或者使用mysqldump --single-transaction保持 InnoDB 一致性,同时减少对线上写入的锁争用。
- 使用
- 索引重建(Rebuild Index)与优化表(OPTIMIZE TABLE)
- 当执行
ALTER TABLE ... ADD INDEX、CREATE INDEX、OPTIMIZE TABLE时,数据库会对整个表做读写重写:- 全量读取表数据到临时表
- 按照索引顺序写入新表
- 交换表文件
- 过程会对磁盘 I/O 造成极大压力,CPU 也会参与排序、压缩页等作业。
- 建议:
- 非高峰期执行这些操作,必要时使用在线 DDL(如 MySQL 5.6+ 的 InnoDB Online DDL)来减少阻塞;
- 对于大表,可先在副本库或从库上完成索引构建,再做主从切换,避免影响主库性能。
- 当执行
2. 自动化脚本与监控任务#
- 定时统计/报表脚本(CRON)
- 一些定时任务需要做大范围数据扫描、统计并写入结果,如果不控制执行时间或分批逻辑,就会在预定时间点集中占用数据库 I/O 与 CPU。
- 建议:
- 将统计任务分散到不同时间段执行(shuffle cron time);
- 对统计任务加限速或分页,避免一次性大查询;
- 在统计时适当使用预聚合表、物化视图,减少每次全表扫描次数。
- 监控巡检/健康检查
- 一些监控工具会定期执行类似
SELECT COUNT(*) FROM table、SHOW TABLE STATUS、SELECT metric FROM information_schema等操作,如果数据库表过大或监控频次过高,也会给 I/O 带来不小压力。 - 建议:
- 调整监控频率,非核心指标可以每 5 分钟或 10 分钟采集一次;
- 使用轻量级的监控接口(如
INFORMATION_SCHEMA小表、performance_schema),避免每次扫描大量业务表。
- 一些监控工具会定期执行类似
3. 复制延迟与从库压力#
- 主从复制压力导致 I/O 争用
- 如果从库用来做报表或开发环境,而你在从库上执行大批量查询,可能会影响从库的 I/O 资源,从而拖慢 Binlog Relay Log 的应用速度,导致复制延迟。主库此时也会因为从库拉取延迟造成更多 Undo/Redo 保留,I/O 和 CPU 都会被拖累。
- 优化:
- 对从库做只读访问,并监控
Seconds_Behind_Master,发现延迟需降低对从库的负载; - 为从库分配独立磁盘(或者使用更高性能存储),避免与主库冲突;
- 或者把报表/分析任务搬到专门的分析型数据库(如 Pentaho、ClickHouse、Elasticsearch),减少对生产库的压力。
- 对从库做只读访问,并监控
五、综合诊断思路#
当监控发现数据库 CPU 持续在 80%~100% 或 I/O 等待(iowait)偏高时,可按以下思路系统性排查:
- 基础监控指标
- CPU 利用率:
top或htop看数据库进程(如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_requestsvs.Innodb_buffer_pool_reads,如果命中率低于 95%,说明缓存不足,需要调大innodb_buffer_pool_size。 - 慢查询日志:开启
long_query_time(如 1s),记录慢查询,并定期分析(pt-query-digest、MySQL Enterprise Monitor)。 - 事务与锁等待:
SHOW ENGINE INNODB STATUS观察当前锁等待情况,如果有大量的 “Waiting for lock” 或 “Semaphore waits”,就要针对锁争用做优化。
- CPU 利用率:
- 定位高耗 SQL
- 使用
SHOW PROCESSLIST查看当前正在执行的语句、执行时间、状态(如 “Copying to tmp table”、“Sorting result”)。 - 对可疑 SQL 做
EXPLAIN分析,看是否走索引、是否触发了 filesort、temporary。 - 定期对慢查询日志做归档与聚合分析,找出 TOP 10 最耗时或最频繁扫描的语句。
- 使用
- 检查索引与表状态
SHOW INDEX FROM table_name检查该表是否缺少常用查询条件的索引。- 利用
ANALYZE TABLE更新统计信息,让优化器能够选择最优执行计划。 - 对于 InnoDB 表,定期执行
OPTIMIZE TABLE(离线或在线)回收碎片,尤其在大量 DELETE/UPDATE 后,表碎片会影响 I/O 与扫描性能。
- 监控系统层面的 I/O/MEM/CPU
- 在 Linux 层面同时监控
vmstat 1、sar -u 1、iostat -dx 1等,准确判断是 CPU 瓶颈还是 I/O 瓶颈。 - 观察磁盘队列长度(
avgqu-sz)、吞吐延迟(await)、利用率(%util),如果%util > 80%且await超过 20ms,就说明磁盘 I/O 可能成为瓶颈,需要考虑升级存储或优化 I/O 调度。 - 监控内存使用和 swap 使用,避免 Buffer Pool 溢出到 Swap,导致 I/O 延迟更高。
- 在 Linux 层面同时监控
- 评估硬件与配置
- 根据业务负载峰值,评估当前 CPU 核数、内存大小、磁盘类型是否能满足需求,尤其在数据量大幅增长后,要及时扩容。
- 检查 MySQL 配置文件(
my.cnf)中:innodb_buffer_pool_size、innodb_log_file_size、innodb_log_buffer_size是否合理;max_connections、thread_cache_size、table_open_cache是否设置过大或过小;- 是否开启了慢查询日志、Performance Schema,以便持续监控。
六、常见优化建议#
1. 针对查询层面#
- 补齐或拆分索引
- 根据慢查询分析结果,对 WHERE 条件常用字段、JOIN 条件字段加联合索引。
- 对于覆盖索引查询(覆盖索引能够包含 SELECT 列),避免回表,减少 I/O。
- 避免函数/表达式在索引列上
- 如果需要对日期按天、月或年分组,建议提前建“年月日”辅助字段,并给辅助字段建索引;
- 或者在查询时用范围条件替代函数,如:
-- 不要写: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. 针对事务与并发#
- 缩短事务粒度
- 优化业务逻辑,让事务尽可能只包含必要的更新/写操作;耗时长的计算、校验尽量放到事务外、异步或批量执行。
- 合理选择隔离级别
- 如果对 脏读(Dirty Read) 可以容忍,选用 Read Uncommitted 或 Read Committed;如果只关心“读到其他事务正在提交之前的数据”,使用 Repeatable Read 即可,无需上升到 Serializable。
- 使用乐观锁或应用层控制
- 对于更新冲突频繁的热点数据,可在应用层用版本号或时间戳控制更新,避免数据库行锁争用。
3. 针对硬件与配置#
- 提高 Buffer Pool 容量
- 让热点数据都能缓存在内存里,降低磁盘随机读次数。
- 对于 SSD,Buffer Pool Size 可设为物理内存的 70%~80%;对于 HDD,建议为 50%~60%。
- 优化 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 次数。
- 调整磁盘 I/O 调度器
- 在 SSD 上使用
noop或deadline调度器,在 HDD 上可考虑deadline以减少磁盘寻道延迟; - 如在 Linux 上可执行:
- 在 SSD 上使用
echo deadline > /sys/block/sda/queue/scheduler
```
4. 对数据库实例做只读分离或水平扩展 - 对于读多写少的业务,可以通过主从复制将读请求分散到多个从库,减轻主库压力; - 对于写入量急剧增大时,考虑分库分表或分布式数据库(如 TiDB、CockroachDB)来做水平扩展。
4. 针对维护与监控#
- 做好慢查询/热点表监控
- 定期生成慢查询报告(使用
pt-query-digest、Percona Toolkit); - 针对频繁读写的热点表(如用户表、订单表),考虑冷热分离、分区表等机制。
- 定期生成慢查询报告(使用
- 维护任务避开高峰期
- 将备份、索引重建、统计脚本等高消耗操作安排在业务低谷时段;
- 对备份任务加 I/O 限速(
ionice)或使用增量备份,减少对正常业务的干扰。
- 手工/自动清理过期数据
- 对历史数据做归档或分区裁剪,将过旧、访问极少的冷数据迁移到归档库,保持主表尽量小。
- 定期清理旧日志、过期缓存、临时表,避免表膨胀影响查询性能。
七、小结#
- 核心原因:数据库出现 CPU 占用过高 多来源于“复杂/低效查询”、“索引失效”、“频繁日志写入”等需要大量计算或磁盘读取的操作;出现 I/O 占用过高 则常因“Buffer Pool 不足”、“Redo Log/Undo Log 频繁落盘”、“大事务/批量操作”等导致磁盘读写压力骤增。
- 综合定位思路:
- 先看系统层面(
iostat、vmstat、top)是 CPU 饱和还是 I/O 阻塞; - 再看数据库层面(慢查询日志、
SHOW PROCESSLIST、SHOW ENGINE INNODB STATUS、EXPLAIN)定位高耗 SQL; - 最后结合架构/分区/硬件配置做全面优化。
- 先看系统层面(
- 持续优化原则:
- 查询先行优化,尽量做到“有索引就不扫表、有缓存就少读盘”;
- 事务与并发控制,让长事务、锁等待尽可能短;
- 硬件与配置匹配,保证 Buffer Pool、Redo Log、IOD 调度等都与业务负载相匹配;
- 监控与告警,定期审计索引使用、慢查询、复制延迟、磁盘健康,及时触发扩容或重构。 只要按照上述思路逐步排查、修复,通常都能有效降低数据库 CPU 与 I/O 的消耗,确保整体服务稳定、响应及时。