可以从库执行统计查询,但需确保主从延迟可控、从库启用read_only、避免触发写操作;OLAP查询需通过中间件或应用层路由至从库;大表统计应优化索引或物化汇总;延迟大时结果不可信,应以从库最大updated_at为截止点。

从库能直接跑统计查询吗?
可以,但得确认几个前提:主从延迟是否在可接受范围、从库是否启用了 read_only=ON(默认开启,但需检查)、以及统计查询会不会意外触发写操作(比如临时表写入 /tmp 或用到 INSERT ... SELECT)。很多线上问题其实不是“不能查”,而是查着查着把从库拖慢了,进而拉高主从延迟,形成负反馈。
- 确保从库没有被其他高负载任务占用(比如备份、ETL 同步)
- 避免在从库执行
CREATE TEMPORARY TABLE 以外的任何写语句,否则会报错 ERROR 1792 (HY000): Cannot execute statement in a READ ONLY transaction
- 如果统计逻辑依赖
SELECT ... INTO OUTFILE,这条路走不通——从库禁止文件导出
怎么让 OLAP 查询自动路由到从库?
MySQL 本身不提供读写分离路由能力,得靠中间件或应用层控制。常见做法是:在连接池或 DAO 层根据 SQL 特征(如是否含 SELECT、是否带聚合函数)决定连主库还是从库;更稳妥的是显式指定只读连接。
- 应用里建立两个数据源:
masterDataSource 和 slaveDataSource,统计类 Service 方法强制使用后者
- 若用
mysql-router 或 ProxySQL,需配置规则匹配 SELECT COUNT(*)、GROUP BY、ORDER BY ... LIMIT 等典型 OLAP 模式,但注意规则别太宽泛,否则把带 SELECT 的更新语句也误判了
- JDBC 连接串加
?readOnly=true 只是提示,不保证路由;真正生效要配合后端权限(比如给从库账号只授 SELECT)
大表 GROUP BY 卡死从库怎么办?
从库硬件通常比主库弱,而统计查询又爱扫全表+建临时内存表,极易打满 tmp_table_size 和 max_heap_table_size,导致落盘到磁盘临时表,IO 直接拉满。
- 查看执行计划:
EXPLAIN FORMAT=TREE 确认是否走了索引下推、有没有 Using temporary / Using filesort
- 强制走覆盖索引:
SELECT /<em>+ USE_INDEX(t, idx_date_status) </em>/ COUNT(*), status FROM t WHERE dt >= '2024-01-01' GROUP BY status
- 临时调大从库参数(仅限低峰期):
SET SESSION tmp_table_size = 512<em>1024</em>1024;,但别永久改,避免挤占 buffer pool
- 更可持续的做法:提前物化统计结果到汇总表,用主库定时任务写,从库只查汇总表
主从延迟大时统计结果还可信吗?
不可信,这是最常被忽略的一点。比如按小时统计订单量,若从库延迟 15 分钟,那 10:00–11:00 的数据永远缺最后 15 分钟,且这个缺口是动态漂移的——你没法靠“等延迟降下来再查”解决,因为业务是持续写的。
- 不要用
SHOW SLAVE STATUS 的 Seconds_Behind_Master 做判断依据,它不准(尤其 GTID 模式下常为 0 但实际有积压)
- 更靠谱的方式:在业务表加
updated_at 字段,统计前先查从库当前最大 updated_at,再拿这个时间戳当统计截止点,相当于“查截至此刻已同步的数据”
- 对实时性要求高的统计场景,宁可主库加索引优化,也不赌从库延迟稳定
CREATE TEMPORARY TABLE 以外的任何写语句,否则会报错 ERROR 1792 (HY000): Cannot execute statement in a READ ONLY transaction
SELECT ... INTO OUTFILE,这条路走不通——从库禁止文件导出SELECT、是否带聚合函数)决定连主库还是从库;更稳妥的是显式指定只读连接。
- 应用里建立两个数据源:
masterDataSource和slaveDataSource,统计类 Service 方法强制使用后者 - 若用
mysql-router或ProxySQL,需配置规则匹配SELECT COUNT(*)、GROUP BY、ORDER BY ... LIMIT等典型 OLAP 模式,但注意规则别太宽泛,否则把带SELECT的更新语句也误判了 - JDBC 连接串加
?readOnly=true只是提示,不保证路由;真正生效要配合后端权限(比如给从库账号只授SELECT)
大表 GROUP BY 卡死从库怎么办?
从库硬件通常比主库弱,而统计查询又爱扫全表+建临时内存表,极易打满 tmp_table_size 和 max_heap_table_size,导致落盘到磁盘临时表,IO 直接拉满。
- 查看执行计划:
EXPLAIN FORMAT=TREE 确认是否走了索引下推、有没有 Using temporary / Using filesort
- 强制走覆盖索引:
SELECT /<em>+ USE_INDEX(t, idx_date_status) </em>/ COUNT(*), status FROM t WHERE dt >= '2024-01-01' GROUP BY status
- 临时调大从库参数(仅限低峰期):
SET SESSION tmp_table_size = 512<em>1024</em>1024;,但别永久改,避免挤占 buffer pool
- 更可持续的做法:提前物化统计结果到汇总表,用主库定时任务写,从库只查汇总表
主从延迟大时统计结果还可信吗?
不可信,这是最常被忽略的一点。比如按小时统计订单量,若从库延迟 15 分钟,那 10:00–11:00 的数据永远缺最后 15 分钟,且这个缺口是动态漂移的——你没法靠“等延迟降下来再查”解决,因为业务是持续写的。
- 不要用
SHOW SLAVE STATUS 的 Seconds_Behind_Master 做判断依据,它不准(尤其 GTID 模式下常为 0 但实际有积压)
- 更靠谱的方式:在业务表加
updated_at 字段,统计前先查从库当前最大 updated_at,再拿这个时间戳当统计截止点,相当于“查截至此刻已同步的数据”
- 对实时性要求高的统计场景,宁可主库加索引优化,也不赌从库延迟稳定
EXPLAIN FORMAT=TREE 确认是否走了索引下推、有没有 Using temporary / Using filesortSELECT /<em>+ USE_INDEX(t, idx_date_status) </em>/ COUNT(*), status FROM t WHERE dt >= '2024-01-01' GROUP BY status
SET SESSION tmp_table_size = 512<em>1024</em>1024;,但别永久改,避免挤占 buffer pool- 不要用
SHOW SLAVE STATUS的Seconds_Behind_Master做判断依据,它不准(尤其 GTID 模式下常为 0 但实际有积压) - 更靠谱的方式:在业务表加
updated_at字段,统计前先查从库当前最大updated_at,再拿这个时间戳当统计截止点,相当于“查截至此刻已同步的数据” - 对实时性要求高的统计场景,宁可主库加索引优化,也不赌从库延迟稳定
事情说清了就结束。










