写点什么

数据库大咖丁奇:MySQL 索引存储顺序和 order by 不一致,怎么办?

  • 2019-11-07
  • 本文字数:2546 字

    阅读完需:约 8 分钟

数据库大咖丁奇:MySQL索引存储顺序和order by不一致,怎么办?

今天与大家分享:当索引存储顺序和 order by 不一致,该怎么办?


根据指定的字段排序来显示结果,是我们写应用时最常见的需求之一了,比如一个交易系统中,按照交易时间倒序显示交易记录。


相信你听说过这样的建议:如果有 order by 的需求,给需要排序的字段加上索引,就可以避免数据库排序操作。


所谓数据库排序操作,是指数据库在执行过程中,先将满足条件的数据全部读出来,放入内存中,再执行快排,这个内存就是 sort_buffer。


如果临时数据量比 sort_buffer 大, 就要把数据放入临时文件,然后做外部排序。


这个排序过程的消耗是比较大的。


所谓避免数据库排序操作,是指执行过程中不需要快排或外部排序。


为什么加上索引就可以避免排序呢?如果索引存储顺序和 order by 不一致,还需要排序吗? 如果是联合索引呢?


今天我们就来说一说,建了索引以后,order by 是怎么执行的以及怎么优化。


为了便于说明,我创建一个简单的表,这个表里,除了主键索引 id 外,还有一个联合索引 ab。你可以在文稿中看到这个表的定义。



我们来看看不同的业务需求下,SQL 语句怎么写,以及在 MySQL 里是怎么执行的。

单字段排序

一个简单的需求是将这个表的数据,按照 a 的大小倒序返回。你的 SQL 语句可以这么写:



我们来看看这个联合索引 ab 的结构。



图 1 索引(a,b)示意图


可以看到,在这个索引上,数据存储顺序是:按照 a 值递增,对于 a 值相同的情况,按照 b 值递增。


因此上面这个语句的执行流程就是:


1.从索引 ab 上,取最右的一个记录,取出主键值 ID_Z;


2.根据 ID_Z 到主键索引上取整行记录,作为结果集的第一行;


3.在索引 ab 上取上一个记录的左边相邻的记录;


4.每次取到主键 id 值,再到主键索引上取到整行记录,添加到结果集的下一行;


5.重复步骤 3、4,直到遍历完整个索引。


可以看到,这个流程中并不涉及到排序操作。我们也可以用 explain 语句来验证这个结论。


图 2 是这个语句的 explain 的结果,可以看到,Extra 字段中没有 Using filesort 字样,说明这个语句执行过程中,不需要用到排序。



图 2 order by 不需要排序

组合字段排序

有了上面的分析,我们再来看看下面这个语句:



这个语句的意思是,按照 a 值倒序,当 a 的值相同时按照 b 值倒序。


你一定发现了,这个语句的执行逻辑和执行结果,跟前面的语句是一模一样的,因此也不需要排序。


倒序不需要排序,正序呢?正序的语句是这么写的:



显然,这个语句也是不需要排序的,执行流程上,只需要先取 ab 索引树最左边的节点,然后向右遍历即可。


到这里我们可以小结一下:


1.InnoDB 索引树以任意一个叶节点为起始点,可以向左或向右遍历;


2.如果语句需要的 order by 顺序刚好可以利用索引树的单向遍历,就可以避免排序操作。

Descending Indexes

接下来我们来看一种不满足”单向遍历“的场景。



这个语句要求查询结果中的记录排序顺序是:按照 a 值正序,对于相同的 a 值,按照 b 值倒序。


由于不满足单向遍历的要求,因此只能选择使用排序操作。


图 3 是这个语句 explain 的结果。



图 3 order by 需要排序


extra 字段中 Using filesort 表示使用了排序。


你一定想到了,如果可以让 InnoDB 在构建索引 ab 的时候,相同的 a 里面,b 能够从大到小排序,就又可以满足单向遍历的要求了。


在 MySQL5.7 及之前的版本是不支持这么创建索引的,在 8.0 版本中支持了这个功能,官方名称是 Descending Indexes。


在 8.0 版本中,我们可以把索引 ab 的定义做个修改。



我们将索引 ab 的定义做了修改,在字段 b 后面加上 desc,表示对于相同的 a 值,字段 b 按照倒序存储。


这个表对应的索引 ab 的结构图如下,点击可以查看大图。




图 4 索引(a, b desc) 示意图 和 explain 的结果


这样从左到右遍历这个索引的时候,就刚好满足 a 正序,然后 b 逆序的要求。


Descending Indexes 可以避免这种情况下的排序操作,语句的执行性能自然就提升了。

应用优化

前面说过,Descending Indexes 这个功能是在 MySQL 8.0 才支持的。那如果你的生产环境上使用的还是低于 8.0 的版本,有没有不需要排序的方法呢?


答案是有的,接下来我给大家介绍一种应用端协作的优化方案。


假设我们现在的需求就是在 MySQL 5.7 版本下,要求按照”a 值正序,然后 b 值逆序”的顺序,返回所有行 a 和 b 的值。


首先,为了避免数据库排序,我们直接执行下面这个语句:



当然,这个语句返回的结果集是不满足业务要求的,但是我们知道,对于相同的 a 值,b 值是有序递增的,我们要把这个数据特点利用起来。


执行这个语句后,应用端的逻辑改造如下:


1.构造一个空栈(stack),栈中的节点可以保存数据行;


2.读入第一行,入栈;


3.读入下一行,


a.如果新一行中 a 值与上一行相同,将新一行入栈;


b.如果新一行中 a 值与上一行不同,则将栈中的所有数据行依次出栈并输出,直到栈清空;然后新一行入栈。


4.重复步骤 3 直到遍历完整个索引,将栈中的所有数据行依次出栈并输出,直到栈清空。


下图 5 是用图 1 中的示例数据,执行上面的流程的效果图。



图 5 应用端优化的执行流程


可以看到,这个过程中数据库端没有使用排序,在应用端也没有使用排序。


这个过程需要在应用端构造一个栈,需要临时内存。当然这个内存并不是凭空多出来的,因为如果不使用这个方法,就只能在 MySQL 端排序,这个内存就会在 MySQL 里创建,也就是 sort_buffer。


相比之下,使用应用端的内存还是比使用 MySQL 的内存好些,也算是这个方案的另一个优点。

小结

接下来,总结一下今天的主要内容。


今天介绍了 MySQL 在有索引的情况下,处理 order by 请求的执行过程,也介绍了 Descending Indexes 的应用背景。


Descending Indexes 是 MySQL 8.0 才支持的特性。在数据库不支持一些特性的时候,也可以考虑通过应用端的协作来实现业务需求。方案优化并不一定只是数据库的优化,综合考虑系统中各个模块的特性,可以增强我们解决问题的灵活性。


作者介绍:


林晓斌,网名丁奇,腾讯云数据库负责人,数据库领域资深技术专家。作为活跃的 MySQL 社区贡献者,丁奇专注于数据存储系统、MySQL 源码研究和改进、MySQL 性能优化和功能改进,在业务场景分析、系统瓶颈分析、性能优化方面拥有丰富的经验。其创作的《MySQL 实战 45 讲》专栏受众已逾 2 万人。


本文转载自公众号云加社区(ID:QcloudCommunity)。


原文链接:


https://mp.weixin.qq.com/s/rYE5lQ9usMPlFe9roqUYlg


2019-11-07 17:583946

评论 1 条评论

发布
用户头像
为什么order by a,b desc 的时候,mysql 干脆整个(a,b)索引都不走了,不是至少a可以用一下吗
2022-02-28 14:49
回复
没有更多了
发现更多内容

软件测试/测试开发 | Web 控件定位与常见操作

测试人

软件测试 自动化测试 测试开发 Web自动化测试

安擎董事长俞跃渊:打造智能算力,创新产业价值

科技热闻

SpringBoot 如何保证接口安全?老鸟们都是这么玩的!

做梦都在改BUG

Java Spring Boot 接口

Svelte框架结合SpreadJS实现表格协同文档

葡萄城技术团队

Java培训有哪些不同的学习方法

小谷哥

大数据培训学习选择哪个机构好

小谷哥

模块6如何设计微服务架构

程序员小张

Sentinel 是如何实现分布式限流的?

做梦都在改BUG

sentinel 分布式限流

宋红康2023版Java视频发布

小谷哥

安势信息入选 SegmentFault思否「2022 中国新锐技术先锋企业」

安势信息

软件成分分析 清源CleanSource SCA 安势信息 技术先锋 SegmentFault

关于使用消息队列今天被面试官问倒了

做梦都在改BUG

热点面试题: Array中有哪些非破坏性方法?

Immerse

JavaScript array 前端面试题 Javascript框架 超全前端面试题

实战分享,电路板设计后这样干,一个人也能轻松搞定项目!

华秋PCB

PCB PCB打样 PCB设计

只用了半个Redisson的Semaphore实现并发控制

做梦都在改BUG

Java 并发控制 Semaphore redisson

活动预告|Triton Meetup 2023

AI Infra

AI

fabric.js开发图片编辑器的细节实现

秦少卫

架构 编辑器 Fabric.js 前端编辑器

大数据培训零基础的方法有哪些

小谷哥

再获权威认证!秒云顺利通过ISO20000、27001双系统认证

MIAOYUN

ISO9001 ISO20000 ISO27001

面试官:谈谈你对JVM内存结构的理解

做梦都在改BUG

Java JVM

软件测试/测试开发 | web 控件的交互进阶

测试人

软件测试 自动化测试 测试开发 Web自动化测试

用户卖家平台三方螺旋成长 如何让商品推荐更智能

阿里技术

全球化技术能力

TDengine 3.0.2.5 查询再优化!揭秘索引文件的工作原理

TDengine

数据库 tdengine 时序数据库

前端培训学习方法有哪些

小谷哥

IoTLink 版本更新 v1.5.2

山东云则信息科技

物联网

新年伊始,谈谈开源软件供应链安全的新趋势

安势信息

开源软件 清源CleanSource SCA 安势信息 ChatGPT 开源软件供应链安全

秒云加入金兰组织,携手共建信创新生态

MIAOYUN

信创 信创云 信创产业 金兰组织

啊啊啊!小程序小游戏也可以在自己的App上架❗️❗️

没有用户名丶

微信小程序 小程序游戏

软件测试/测试开发 | 网页 frame 与多窗口处理

测试人

软件测试 自动化测试 测试开发 Web自动化测试

上架的时候怎么向某个版本添加构建版本

雪奈椰子

apple ios开发

探讨丨传统行业必须数字化转型吗?

优秀

数字化转型

银行业上云进行时,OLAP 云服务如何解决传统数仓之痛?

Kyligence

OLAP技术 传统数仓

数据库大咖丁奇:MySQL索引存储顺序和order by不一致,怎么办?_数据库_林晓斌_InfoQ精选文章