MySQL性能优化实战:慢查询到毫秒响应全链路突破
|
MySQL性能优化实战:慢查询到毫秒响应全链路突破——这标题不是口号,是我去年10月份在XX电商中台的真实战场。那会儿订单履约系统里一个SELECT COUNT() FROM order_detail WHERE status IN (1,3,5) AND create_time > '2023-09-01'的查询,P95响应卡在4.7秒,高峰期每分钟触发237次慢日志告警,DB CPU长期飙到92%。
文章配图,仅供参考 我拉出slow_log发现,这个语句实际执行计划走的是全表扫描——因为create_time字段没索引,而status只有3个离散值,MySQL优化器直接放弃用status索引。更讽刺的是,这张表有1.8亿行,但每天新增才12万,冷热数据分明。我加了(status, create_time)联合索引?不,错了三次:第一次建了(create_time, status),范围查询+IN还是失效;第二次用覆盖索引但忘了include order_id——应用层需要回表取ID做分页;第三次才定版(status, create_time, order_id),配合SQL重写为WHERE status IN (1,3,5) AND create_time >= '2023-09-01' AND create_time < '2023-10-01',响应压到68ms。 但问题没完。上周监控又报凌晨2:17突发5秒延迟——查出来是ETL任务在凌晨调度时执行了ALTER TABLE order_detail ADD COLUMN remark VARCHAR(512) DEFAULT '' AFTER status,虽加了ALGORITHM=INSTANT,可MySQL 8.0.23有个隐藏bug:当表存在全文索引+大量二级索引时,INSTANT DDL会偷偷触发元数据锁排队。我抓了pt-deadlock-logger,发现它和两个正在执行的SELECT ... FOR UPDATE事务形成锁等待链。删掉冗余的FULLTEXT索引后,同样的ADD COLUMN耗时从3.2秒降到89ms。 MySQL性能优化实战:慢查询到毫秒响应全链路突破 这个过程里最让我拍桌子的是Query Rewrite插件——官方文档写“支持重写任意DML”,可实测中它对带子查询的UPDATE根本没反应,必须手动把UPDATE t1 SET x=(SELECT y FROM t2 WHERE t2.id=t1.ref_id)拆成两步临时表。我们团队花了两天写Python脚本解析general_log自动匹配SQL模板,硬是把17类高频劣质SQL注入rewrite规则库,比如把DATE(create_time) = '2023-10-01'自动转成create_time >= '2023-10-01 00:00:00' AND create_time < '2023-10-02 00:00:00'。上线三天,慢查询率从日均1128次降到21次——但这玩意儿在MySQL 8.0.33之前不支持参数化模式匹配,每次改业务逻辑就得重刷整个规则表。 去年10月份,我亲眼看着一个原本依赖Redis缓存兜底的库存校验接口,在去缓存化改造中暴露真面目:它的JOIN四张表(product, sku, stock, warehouse)的SQL,因warehouse.name字段未加索引且LIKE '%华东%'导致NLJ退化成BNL,连接内存buffer溢出触发磁盘临时表。改成warehouse.code='EASTCHN'后,执行时间从2.1秒跳变到18ms。可这里有个细节没人提:MySQL 8.0.29起,optimizer_switch='block_nested_loop=off'其实默认生效,但如果你的my.cnf里显式写了block_nested_loop=on——恭喜,你亲手关掉了这个优化开关。 新技术真香 MySQL性能优化实战:慢查询到毫秒响应全链路突破 不过得老实承认:我把Buffer Pool从16G加到32G之后,QPS只涨了3%,而把innodb_log_file_size从48M调大到256M却让redo刷盘抖动下降73%——这种反直觉效果,靠理论模型根本推不出来。我现在手边还堆着三个没解决的case:一个是分区表跨月查询的统计偏差,另一个是Group Replication下GTID gap导致从库复制延迟毛刺,第三个…算了,下周约Oracle ACE喝咖啡碰碰看? (编辑:站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |

