mysql升级后qps下降的真正原因是执行计划突变、配置未同步及校对规则变更,需重点检查explain变化、buffer_pool_chunk_size对齐、binlog压缩开关及utf8mb4校对规则差异。

MySQL升级后QPS掉了一半?先别急着回滚,看慢查询是否被重写
MySQL大版本升级(比如5.7→8.0)常导致执行计划突变,EXPLAIN结果里type从ref退化成ALL、key_len变小、rows暴涨——这些才是压测QPS下跌的真正线索。
实操建议:
- 升级前导出所有高频SQL的
EXPLAIN FORMAT=TRADITIONAL结果,重点比对key、rows、Extra字段 - 8.0默认启用
optimizer_switch='index_merge=on',某些旧索引组合可能被绕过,用SET optimizer_switch='index_merge=off'临时验证 - 注意
sql_mode变更:8.0默认含STRICT_TRANS_TABLES,部分隐式转换失败会直接报错,而非静默截断
用pt-query-digest对比升级前后慢日志,但必须关掉query rewrite
Percona Toolkit的pt-query-digest能聚合分析慢日志,但MySQL 8.0默认开启performance_schema的语句重写(如把SELECT * FROM t转成带库名的完整形式),会导致相同逻辑SQL被识别为不同指纹,对比失真。
实操建议:
- 升级前在测试库执行
SET GLOBAL performance_schema_digests_size = 0,或启动时加--performance-schema-digests-size=0 - 用
pt-query-digest --no-report --filter '$event->{fingerprint} =~ m/^SELECT|^UPDATE|^INSERT/'过滤出核心DML,避免统计干扰 - 对比时盯住
Query_time的95分位和Rows_examined中位数,这两个比平均值更能反映真实毛刺
sysbench压测结果不准?检查innodb_buffer_pool_chunk_size和预热方式
MySQL 5.7默认innodb_buffer_pool_chunk_size=1M,8.0升到128M;如果升级后没调大innodb_buffer_pool_size,实际可用内存可能反而下降,导致缓存命中率暴跌——这时压测TPS虚低,不是引擎问题,是配置没跟上。
实操建议:
- 升级后立刻查
SHOW VARIABLES LIKE 'innodb_buffer_pool_chunk_size',按公式innodb_buffer_pool_size / innodb_buffer_pool_chunk_size必须是整数,否则chunk无法对齐 - sysbench预热不能只跑
oltp_read_only,要先用--time=300 --threads=32 --events=0跑满5分钟,再清空innodb_buffer_pool_pages_data确认缓存已刷空 - 对比时用同一份
sbtest数据,避免8.0默认innodb_file_per_table=ON导致.ibd文件分布差异影响IO
主从延迟飙升?检查replica_parallel_workers和binlog_transaction_compression
MySQL 8.0.22+默认开启binlog_transaction_compression=ON,虽然减少网络传输量,但压缩/解压耗CPU;若从库CPU已饱和,反而加剧延迟。同时replica_parallel_workers在8.0默认为4,但若主库事务粒度细(比如大量单行UPDATE),并行线程争抢锁可能拖慢整体回放。
实操建议:
- 升级后立即检查
SHOW VARIABLES LIKE 'binlog_transaction_compression',生产环境建议设为OFF,用网络带宽换CPU稳定性 - 观察
SHOW REPLICA STATUS\G里的Seconds_Behind_Master和Replica_SQL_Running_State,若长期卡在Waiting for dependent transaction to commit,说明并行策略不匹配,需调低replica_parallel_workers到1~2 - 对比压测期间的
Threads_connected峰值,8.0连接池管理更激进,高并发下max_connections不足会触发拒绝连接,掩盖真实性能瓶颈
最易被忽略的是字符集校对规则变更:utf8mb4_0900_as_cs和utf8mb4_general_ci在JOIN和ORDER BY时行为不同,连EXPLAIN都看不出区别,只能靠业务SQL实际跑一遍结果集比对。











