从库sql线程重放变慢主因是select查询争用innodb缓冲池,挤出热数据页致频繁读盘;同时长查询阻塞元数据锁、cpu调度不足及buffer_pool配置偏低亦加剧延迟。

从库查询争用InnoDB缓冲池,拖慢SQL线程重放
从库上跑的SELECT查询和SQL线程共用innodb_buffer_pool_size,如果查询量大、扫描范围广,会把热数据页挤出缓冲池,导致SQL线程回放时频繁读盘——这不是“查询慢”,而是“回放变慢”。
常见现象:SHOW PROCESSLIST里SQL线程状态反复出现Waiting for table metadata lock或Reading event from the relay log,同时iostat -x 1显示relay log所在磁盘%util持续 >90%。
- 确认是否启用
read_only=ON:没开的话,应用误发写操作会触发锁竞争,直接卡住SQL线程 - 检查
innodb_buffer_pool_size是否与主库一致:从库常被低估配置,建议至少设为主库的80% - 禁用已废弃的
query_cache_type(MySQL 5.7+默认关闭,但旧配置残留仍可能干扰)
长查询阻塞元数据锁,让DDL重放排队
从库SQL线程回放DDL(如ALTER TABLE)时需获取metadata lock,而此时若有长SELECT正在扫描同一张表,DDL就会一直等待——单线程下后续所有事务全部堵死。
典型表现:Seconds_Behind_Master突增,SHOW PROCESSLIST中system用户线程状态为Waiting for table metadata lock,且Time值远高于其他线程。
- 用
SELECT * FROM performance_schema.threads WHERE TYPE = 'FOREGROUND'查活跃前台连接,结合PROCESSLIST_INFO定位长查询 - 避免在从库执行
SELECT * FROM huge_table类全表扫描;加WHERE条件并确保有索引 - 业务低峰期再执行DDL,或改用
ALGORITHM=INPLACE的Online DDL
并发查询耗尽CPU,让SQL线程抢不到调度权
MySQL 5.6–5.7默认SQL线程是单线程,且不设优先级。当从库CPU满载(比如大量JOIN或GROUP BY),OS调度器可能长时间不分配时间片给SQL线程。
验证方式:top -H -p $(pgrep mysqld)看mysqld进程内各线程CPU占用,若SQL线程(thread id可从performance_schema.threads查)长期%CPU
- 限制应用查询并发数,别让从库当“查询黑洞”
- MySQL 5.7+开启并行复制:
SET GLOBAL slave_parallel_workers = 4,但必须配slave_parallel_type = LOGICAL_CLOCK - 别在从库开
slow_query_log写磁盘——IO压力会反向拖慢SQL线程
查询触发临时表或排序,吃掉系统内存资源
复杂查询生成的tmp_table_size或sort_buffer_size过大,会导致从库物理内存不足,触发swap——SQL线程重放binlog时分配内存失败,直接降速甚至OOM kill。
现象:dmesg | grep -i "killed process"能看到mysqld被杀记录,SHOW STATUS LIKE 'Created_tmp%'值异常高。
- 调低
tmp_table_size和max_heap_table_size(建议≤64M),逼查询走磁盘临时表而非内存,避免OOM - 用
EXPLAIN检查从库上高频查询的Extra字段,含Using temporary或Using filesort的必须优化 - 监控
Threads_connected峰值,超200连接时考虑加读负载均衡,别全压到一台从库
真正容易被忽略的是:从库不是“只读副本网关”,它是带状态的复制执行引擎。任何对它的查询,都在和SQL线程争夺同一套底层资源——缓冲池、CPU时间片、内存页、IO队列。想靠“加几台从库”解决延迟,不如先管住自己往从库发的那几条慢查询。











