prefer_ordering_index 是 MySQL 8.0.21 版本引入的优化器开关参数,通过 optimizer_switch 系统变量控制,其核心作用在于调整优化器对"排序索引"的偏好程度,直接影响执行计划的选择逻辑。
官网文档:https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-21.html
1. 为什么这个参数的设置如此重要?优化器决策的核心矛盾:当查询语句包含 WHERE 过滤条件和 ORDER BY 时,优化器面临两难选择:
选择排序索引:使用能避免额外排序的索引(如 ORDER BY 字段的索引),但可能需要扫描大量不满足 WHERE 条件的数据
选择过滤索引:使用能高效过滤数据的索引(如 WHERE 字段的索引),但需要额外进行排序操作
系统默认:prefer_ordering_index=on(默认)会优先选择排序索引,在高过滤性查询中导致全表扫描,性能可能下降好几倍。
2. 底层机制介绍MySQL 优化器通过成本模型计算执行计划公式:
prefer_ordering_index 通过调整排序成本的权重影响决策:
开启 (on):排序成本权重 × 1.5 → 优化器更倾向避免排序关闭 (off):排序成本权重 × 0.5 → 优化器更倾向高效过滤说明:当 WHERE 条件过滤性 > ORDER BY 排序性时(如 status='shipped' 过滤掉 80% 数据),关闭此参数能显著提升性能。
3. 与 EXPLAIN 的深度关联使用 EXPLAIN FORMAT=JSON 可清晰看到参数影响:
EXPLAIN FORMAT=JSON SELECT * FROM orders WHERE status = 'shipped' ORDER BY create_time DESC LIMIT 10;开启参数:possible_keys 显示 idx_create_time,Extra 显示 Using index condition; Using filesort
关闭参数:possible_keys 显示 idx_status,Extra 显示 Using where; Using index
诊断技巧:当 Extra 出现 Using filesort 且 rows 值远大于 filtered 时,很有可能是 prefer_ordering_index 未优化。
二、创建测试表与数据脚本以下脚本创建包含 100 万行数据的测试表,模拟电商订单场景:
测试脚本说明:
status 字段 80% 为 pending,20% 为 shipped(高过滤性场景)
create_time 覆盖 1 年时间范围(模拟真实业务)
数据量 100 万行(足够展示性能差异)
三、三个实战案例深度分析案例 1:电商订单分页查询(高过滤性场景)问题:获取用户订单的倒序分页(第 1000 页,每页 100 条)
SELECT * FROM orders WHERE status = 'shipped' ORDER BY create_time DESC LIMIT 100 OFFSET 99900;默认行为(prefer_ordering_index=on):
优化后(prefer_ordering_index=off):
SET SESSION optimizer_switch = 'prefer_ordering_index=off';SELECT * FROM orders WHERE status = 'shipped' ORDER BY create_time DESC LIMIT 100 OFFSET 99900;优化执行计划:
性能:0.12 秒(扫描 2 万行,减少 90% I/O)
关键改进:优化器选择 idx_status 索引过滤 20% 数据,再排序
案例 2:用户行为日志统计(GROUP BY 优化)问题:统计最近 7 天各 IP 的登录次数
SELECT ip, COUNT(*) AS cnt FROM login_logs WHERE login_time > DATE_SUB(NOW(), INTERVAL 7 DAY)GROUP BY ip;表结构:
CREATE TABLE login_logs ( id BIGINT PRIMARY KEY, ip VARCHAR(15), login_time DATETIME, INDEX idx_ip_time (ip, login_time), INDEX idx_time_ip (login_time, ip));默认行为(prefer_ordering_index=on):
优化器选择 idx_time_ip(因 ORDER BY 未显式出现,但 GROUP BY 隐含排序)Extra 显示 Using index; Using filesort → 需额外排序优化方案:
SET SESSION optimizer_switch = 'prefer_ordering_index=off';优化后效果:
优化器选择 idx_ip_time 索引Extra 显示 Using index → 避免排序(因索引顺序匹配 GROUP BY)性能提升:查询时间从 2.8 秒 → 0.3 秒(8.7 倍提升)案例 3:商品评论 TOP-N 查询(高排序性场景)问题:获取评分最高的 20 条评论
SELECT * FROM product_comments WHERE product_id = 1001 ORDER BY score DESC LIMIT 20;表结构:
CREATE TABLE product_comments ( comment_id BIGINT PRIMARY KEY, product_id BIGINT, score TINYINT, content TEXT, INDEX idx_product_score (product_id, score));默认行为(prefer_ordering_index=on):
问题:优化器选择主键索引,需扫描 100 万行后排序
优化方案:
SET SESSION optimizer_switch = 'prefer_ordering_index=off';优化执行计划:
性能:0.02 秒(扫描 10 行,避免全表扫描)
关键改进:利用 idx_product_score 覆盖索引,直接返回排序结果
四、最佳实践与总结何时关闭 prefer_ordering_index?场景特征
推荐设置
理由
WHERE 条件过滤性强
off
高效过滤 > 排序成本
分页深度大(OFFSET > 1000)
off
避免全表扫描
GROUP BY 需排序
off
利用索引顺序避免 filesort
ORDER BY 字段区分度低
off
排序成本占比高
何时保持默认(on)?ORDER BY 字段区分度极高(如时间戳)
WHERE 条件过滤性弱(如 WHERE status IN ('all'))
查询返回结果集小(LIMIT 10)
性能验证标准-- 检查排序操作SHOW STATUS LIKE 'Handler_sort';-- 对比优化前/后SET @start = NOW();SELECT ...;SELECT TIMESTAMPDIFF(MICROSECOND, @start, NOW()) AS duration;终极建议:在业务高峰期,对核心查询进行 prefer_ordering_index=off 优化,通常能获得 5-10 倍的性能提升,且无需修改表结构或索引。这是 MySQL 8.0 中被严重低估的查询优化技巧!
五、为什么这个参数如此冷门?默认值陷阱:MySQL 默认开启,大部分开发者误以为这是最优配置。
优化器黑河:很多DBA通常只关注 EXPLAIN 的 type 和 key,忽略 optimizer_switch
场景特定性:仅在特定查询模式下有效(高过滤性 + ORDER BY)
行动建议:
下次遇到 ORDER BY 查询性能问题,先检查:
EXPLAIN 中是否出现 Using filesortWHERE 条件是否能高效过滤数据尝试 SET SESSION optimizer_switch = 'prefer_ordering_index=off' 验证。
京公网安备11000000000001号
京ICP备11000001号
还没有评论,来说两句吧...