从库CPU或IO持续飙高但延迟不大,通常是读请求过载所致。需用pt-query-digest分析慢日志、检查读写分离配置、合理设置权重与连接参数,并监控长事务及锁等待。

从库 CPU 或 IO 持续飙高,SHOW SLAVE STATUS 延迟却不大?
这大概率不是同步本身卡住,而是业务读请求全打到从库,把资源吃满了。MySQL 主从架构里,从库只负责回放 binlog,不承担写压力,但一旦被当“只读集群”用,查询并发一上来,CPU、磁盘 IO、连接数全会顶满,反过来拖慢 SQL 线程执行——Seconds_Behind_Master 看着正常,实际 Exec_Master_Log_Pos 更新变慢,隐性延迟在积累。
- 先确认是不是读压导致:用
pt-query-digest分析从库慢日志,重点看SELECT占比和平均执行时间 - 检查应用层是否硬编码了从库地址,或中间件(如
ShardingSphere、MyCat)的读写分离策略没配权重,所有读请求默认 100% 走从库 - 别只看
Threads_running,更要盯Threads_connected和innodb_row_lock_waits——高并发读也可能触发行锁争用(尤其带FOR UPDATE或非覆盖索引的 SELECT)
配置 read_weight 或 weight 参数时,数值代表什么?
权重不是百分比,而是相对比例。比如主库设 weight=10,两个从库分别设 weight=5 和 weight=1,那读请求按 10:5:1 分发——主库也会分到读流量(除非显式禁用)。很多团队误以为设成 0 就能彻底隔离读写,但多数中间件(包括 ProxySQL 的 mysql_servers.weight)中 0 表示“不参与负载均衡”,而非“禁止访问”;真要主库完全不读,得配合 hostgroup 或路由规则过滤。
-
ProxySQL中修改后必须执行LOAD MYSQL SERVERS TO RUNTIME,否则不生效 -
ShardingSphere-JDBC的readwrite-splitting配置里,load-balancer-name设为ROUND_ROBIN时,权重才起作用;设成random则忽略权重 - 权重值过大(如 1000)可能导致小权重节点长期收不到请求,建议控制在 1–100 区间内做微调
max_connections 和 wait_timeout 在从库上怎么调才不翻车?
读请求多,连接数暴涨是常态。但从库的 max_connections 不能盲目拉高——它直接决定 MySQL 预分配的内存总量(每个连接至少占用 sort_buffer_size + read_buffer_size + thread_stack),容易引发 OOM;而 wait_timeout 设太长,又会让大量空闲连接滞留,占着线程和内存不放。
- 从库
max_connections建议不超过主库的 1.5 倍,优先通过连接池(如HikariCP的maximumPoolSize)控源头 -
wait_timeout推荐设为 60~180 秒,比应用层连接池的idleTimeout小 10 秒,避免连接池认为连接还活着、MySQL 却已断开 - 务必关掉
skip_name_resolve,否则高并发下 DNS 反查会显著拖慢连接建立,加剧连接堆积
监控里看到 Slave_SQL_Running_State 长期卡在 “Reading event from the relay log”?
这不是 relay log 读取慢,而是 SQL 线程在等事务提交或锁资源。典型诱因是大事务回放(比如主库一个 UPDATE 改了 50 万行)、或者从库上有长事务没提交(SELECT ... FOR UPDATE 持锁),导致后续 relay log 里的语句被阻塞。
- 用
SELECT * FROM information_schema.INNODB_TRX ORDER BY TRX_STARTED LIMIT 5查当前长事务,重点关注TRX_STATE = 'LOCK WAIT'和TRX_MYSQL_THREAD_ID - 别直接 kill,先看
information_schema.PROCESSLIST对应线程在执行什么 SQL;如果是业务查询,联系开发加索引或拆分 - 主库端控制单事务数据量,避免
INSERT INTO ... SELECT或未分批的DELETE;从库可开启slave_preserve_commit_order=ON(MySQL 5.7+),减少并行回放时的锁冲突
真正难处理的是混合负载场景:既要扛住报表类慢查询,又要保证核心接口的低延迟读取。这时候光调权重和连接参数不够,得在 SQL 层做分级——比如给报表查询强制走特定从库(用 /*+ db_name@slave2 */ 注释),而不是让所有读都挤在同一套实例上。



















