order by limit 造成优化器选择索引错误

 1.order by limit 选错索引示例

在日常工作中,经常发现一些简单的查询语句因为加了 order by limit 造成优化器选择索引错误。例如如下sql(此处就不造数据了,只是列出一个sql示例)

select * from test_table where name='xxxxx' order by id limit 1;

#id 列为表的主键,name列有索引。有可能该查询最高效的路径应该是使用 name 索引(name 列为前缀的组合索引)访问,但是优化器最后却选择通过主键id进行扫描。这样就可能导致查询很低效(当然查询也可能很高效,这个跟表中数据的具体分布有关)

2.order by limit 为什么会导致选错索引

 在 optimize trace中我们看到有这样一块

{
            "reconsidering_access_paths_for_index_ordering": {
              "clause": "ORDER BY",
              "steps": [
              ],
              "index_order_summary": {
                "table": "`t_test_part12`",
                "index_provides_order": true,
                "order_direction": "desc",
                "index": "idx_create_time",
                "plan_changed": true,       ##在 reconsider之前选择的是条件列上的索引,只需要扫描部分数据,这里的true表示访问路径发生了改变(通过排序索引顺序扫描)
                "access_type": "index"
              }
            }
          }

##这里的重的选择索引在源码中对应到sql/sql_select.cc中如下

test_if_cheaper_ordering(const JOIN_TAB *tab, ORDER *order, TABLE *table,
                         key_map usable_keys,  int ref_key,
                         ha_rows select_limit,
                         int *new_key, int *new_key_direction,
                         ha_rows *new_select_limit, uint *new_used_key_parts,
                         uint *saved_best_key_parts)
sql_select.cc 在5.7时就有4400多行,我们不需要刻意去撸通它,但是关于 test_if_cheaper_ordering 有一段注释值得我们去关注,这段注释如下:
 /*
           We assume that each of the tested indexes is not correlated
           with ref_key. Thus, to select first N records we have to scan
           N/selectivity(ref_key) index entries.
           selectivity(ref_key) = #scanned_records/#table_records =
           refkey_rows_estimate/table_records.
           In any case we can't select more than #table_records.
           N/(refkey_rows_estimate/table_records) > table_records
           <=> N > refkey_rows_estimate.
          */

##首先假设被检查的索引同ref_key没有关系。其次假设数据是均匀分布的。如果数据是均匀分布的,那么通过排序索引访问前N行数据(order by limit N)需要扫描的行数为 N*(table_records/distinct(ref_key))
这两个假设是估算通过排序索引来访问cost 的前提(但是现实生产中这两个假设在绝大多数场景中都是不成立的,所以就造成多数场景下索引选择错误)


3.问题处理

  第二部分中我们知道,因为 test_if_cheaper_ordering() 假设的前提在生产环境中通常不会成立,所以就造成该类sql很多时候生成的都不是最优执行计划

##生产上遇到过通过条件索引过滤执行时间为几十毫秒,但是通过索引排序扫描耗时1小时的案例

3.1 处理方案1

主动关闭该优化器开关  prefer_ordering_index

## 该问题已经被确认为bug,但是mysql目前为止没有给出具体的优化方案,但是他们提供了一个优化器开关(mysql 8.0.21及之后版本),让用户自己选择是否需要该类型的优化,如果用户认为该类优化会导致错误的执行计划,可以主动关闭该类优化。优化器开关名为 (默认打开,可以根据需要进行关闭)

3.2 处理方案2

重写 sql ,比如上面提到的 sql ,select * from test_table where name='xxxxx' order by id limit 1;

我们业务逻辑可以分两步处理

1)select min(id) from test_table where name='xxxxx';  ##取出符合条件的主键id

2)通过第一步中得到的主键id 去获取相关信息

select * from test_table where id=11;

3.3 处理方案3

通过 hint 固化执行计划。比如通过 force index 指定使用的索引

4.相关文档

on ORDER BY optimization – domas mituzas

MySQL Bugs: #97001: Dangerous optimization reconsidering_access_paths_for_index_ordering

MySQL :: WL#13929: Introduce new optimizer switch to disable limit optimization

MySQL · 捉虫动态 · order by limit 造成优化器选择索引错误

mysql回表致索引失效_祈雨v的博客-CSDN博客_回表走索引吗

 

  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

渔夫数据库笔记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值