明明是同一条SQL,为什么有时候走索引a,有时候却走索引b ?
当前位置:点晴教程→知识管理交流
→『 技术文档交流 』
前言想象你是一家餐厅的服务员,面前有两个菜单:
当顾客说:"我要最便宜的川菜"。 你会: 先用菜单B找到所有低价菜 从中筛选川菜
先用菜单A找到所有川菜 再按价格排序
明明是同一条SQL,有时候走的索引a,而有时候走的索引b,就是它的锅。 一个让程序员崩溃的案例现在有个需求:查询今年开始已付款的前100个订单。 给status字段创建了索引idx_status。 给create_time字段创建了索引idx_create_time。 查询订单的sql如下: 周一执行计划如下: 周二执行计划如下:
周一只扫描了500行数据,而周二却扫描了50万行数据。 周一耗时0.1秒,而周二耗时却又8秒。 同一SQL在不同时间性能差异80倍! 让我们拆解背后的原因。 揭秘优化器的"决策三步曲"MySQL优化器的决策流程如下:
成本计算示例: 根据扫描行数、回表次数、排序成本,计算一个总成本的分数。 优化器会选择总成本更低的idx_create_time索引。 导致索引切换的四大真凶真凶1:数据分布变化场景还原:
可能会影响总成本的分数。
代码高亮:
SELECT COUNT(*) AS total, SUM(status='paid') AS paid_count, SUM(create_time>'2023-01-01') AS new_orders FROM orders; 真凶2:统计信息过期统计信息过期,就像用去年的地图导航,新修的路不会出现在地图上。 MySQL的"地图"就是统计信息。 我们可以通过ANALYZE TABLE ... DELETE STATISTICS命令删除统计信息: 这时候查询可能变成全表扫描:
显示type: ALL
使用ANALYZE TABLE命令,刷新统计信息(相当于更新地图): 真凶3:索引覆盖度差异点餐类比:
下面的SQL会走idx_status(需要回表): 代码高亮:
SELECT * FROM orders WHERE status='paid'; 下面的SQL会走idx_create_time(覆盖索引): 真凶4:索引碎片化索引碎片化就像书本的目录页被撕破,找内容变得困难。 检查方法: 查看Data_free字段,值越大碎片越多。 优化方案: 使用ALTER TABLE命令重建索引。 最近建了一些工作内推群,各大城市都有,欢迎各位HR和找工作的小伙伴进群交流,群里目前已经收集了不少的工作内推岗位。加苏三的微信:li_su223,备注:所在城市,即可进群。 问题排查四步法第一步:查看当前执行计划使用EXPLAIN查看当前SQL的执行计划: 代码高亮:
EXPLAIN SELECT * FROM orders WHERE status='paid' AND create_time>'2023-01-01'; 第二步:检查统计信息使用SHOW INDEX命令检查索引的统计信息: 关注Cardinality字段,值越接近真实数据越好。 第三步:分析数据分布使用下面的SQL分析数据分布: 第四步:追踪优化器思考过程
开启optimizer_trace,然后通过INFORMATION_SCHEMA.OPTIMIZER_TRACE表查看追踪优化器思考过程。 三大终极解决方案方案1:引导优化器选择使用FORCE INDEX强制使用指定索引: 代码高亮: SELECT * FROM orders FORCE INDEX(idx_status) WHERE ...; 方案2:创建更优索引创建更优的联合索引:
总结六个必须检查的点
三条黄金法则
参考文章:原文链接 该文章在 2025/12/5 9:43:28 编辑过 |
关键字查询
相关文章
正在查询... |