从库负载高于主库是因读写分离下从库承担读、复制、分析三重压力;并行复制线程数过高或缺失索引会导致io/cpu双高,需合理设置slave_parallel_workers并补全索引。

从库系统负载比主库高,不是配置错了,而是读写分离架构下典型的资源错配现象——主库扛写,从库扛读+复制+分析,三重压力叠在一起。
从库执行SELECT会卡住复制线程?先看并行复制是否过载
MySQL 5.7+ 启用 slave_parallel_workers 后,协调器(Coordinator)要分发 relay log event 给多个工作线程。如果设得太高(比如 slave_parallel_workers = 16),但 CPU 核数只有 4 或磁盘 I/O 弱,就会出现大量线程卡在 Waiting for an event from Coordinator 状态。
- 查当前值:
SELECT @@slave_parallel_workers; - 临时调低测试:
STOP SLAVE; SET GLOBAL slave_parallel_workers = 4; START SLAVE;(建议 ≤ CPU 核数 × 0.8) - 别忘了配
slave_preserve_commit_order = ON,否则降 worker 数可能引发一致性风险
从库慢查询没索引,全表扫描把IO和CPU一起拉爆
主库可能只跑带主键的写操作,而报表、后台导出、监控拉取这些 heavy SELECT 全堆在从库——但对应的索引根本没同步过去。结果就是 Handler_read_rnd_next 暴涨、innodb_buffer_pool_reads 居高不下,磁盘 IO 和 CPU 双高。
- 开慢日志:
SET GLOBAL log_slow_slave_statements = ON; - 抓典型慢查后执行:
EXPLAIN FORMAT=TREE SELECT ...,重点看filtered是否 rows 是否远超实际返回行数 - 对比主从
SHOW CREATE TABLE输出,确认KEY定义一致;注意FULLTEXT和SPATIAL索引不复制,必须手动补
从库内存“看着满”,其实不是 buffer pool 配小了
innodb_buffer_pool_size 和主库一样,不代表用得少。从库无写入干扰,缓存更干净、更易填满;主库因持续写入触发页刷新、LRU 调整,反而常“空着”。真实占用得看 information_schema.innodb_buffer_pool_stats,不是看配置值。
- 查真实用量:
SELECT ROUND(data_size/1024/1024/1024,2) AS used_gb FROM information_schema.innodb_buffer_pool_stats; - 查配置上限:
SHOW VARIABLES LIKE 'innodb_buffer_pool_size'; - 若从库
used_gb接近上限、主库才 60%~70%,说明是使用模式差异,不是该调大 buffer pool - 额外内存开销藏在
slave_pending_jobs_size_max(并行复制预取事件)、sort_buffer_size(分析类查询)、tmp_table_size(大临时表)里,它们不计入 buffer pool,但推高VmRSS
read_only=ON 不等于绝对安全,误写从库才是最隐蔽的负载源
很多“从库高负载”本质是应用把从库当主库用了。即使 read_only = ON,root 或有 SUPER 权限的账号仍可绕过;更常见的是 ORM 自动发 SET SESSION sql_log_bin = 0 关闭 binlog 后直接写从库。
- 查非复制写入:
SELECT * FROM information_schema.PROCESSLIST WHERE COMMAND != 'Sleep' AND USER NOT IN ('system user', 'replication'); - 重点关注
INFO列是否含INSERT/UPDATE/DELETE,以及USER是否为开发或运维账号 - 临时堵漏:
SET GLOBAL read_only = ON; SET GLOBAL super_read_only = ON;(后者防 SUPER 用户绕过)
真正难处理的,是从库上跑的分析类查询——它们不走缓存、不走索引、临时表巨大,且往往和复制线程争抢 I/O 和内存。这类负载不会出现在 SHOW PROCESSLIST 的明显长事务里,却能把 await 和 %util 拉到 100%。











