滴滴一面:order by 调优10倍,思路是啥?

news2025/6/8 6:50:15

背景说明:

Mysql调优,是大家日常常见的调优工作。

所以,Mysql调优是一个非常、非常核心的面试知识点

在40岁老架构师 尼恩的读者交流群(50+)中,其相关面试题是一个非常、非常高频的交流话题。

近段时间,有小伙伴面试滴滴,说遇到一个order by 调优的面试题:

order by 线上的查询速度太慢, 需要优化10倍以上, 说说你的思路?

社群中,还遇到过大概的变种:

形式1:order by 是怎么实现排序的?

形式2:order by 是怎么实现优化的?

形式3: 后面的变种,应该有很多变种…,会收入 《尼恩Java面试宝典》。

这里尼恩给大家order by 调优,做一下系统化、体系化的梳理,使得大家可以充分展示一下大家雄厚的 “技术肌肉”,让面试官爱到 “不能自已、口水直流”

也一并把这个 题目以及参考答案,收入咱们的《尼恩Java面试宝典》,供后面的小伙伴参考,提升大家的 3高 架构、设计、开发水平。

注:本文以 PDF 持续更新,最新尼恩 架构笔记、面试题 的PDF文件,请从这里获取:码云


回答这个order by的优化之前,首先要给面试官介绍一下order by的底层原理。

首先、什么是Order by 工作原理?

假设,有一个用户表为例,表结构如下:

create table `user` (
  `id` int(11) not null auto_increment COMMENT 'id',
  `city` varchar(16) not null COMMENT '城市',
  `name` varchar(16) not null COMMENT '姓名',
  `age` int(11) not null COMMENT '年龄',
  `sex` int(1) default 1 COMMENT '性别',
  primary key (`id`),
  key `city` (`city`)
) engine = InnoDB comment '用户表';

表数据示例如下:

现在假定有个需求:查询前 5 个来自北京的用户姓名、年龄、城市,并且按照年龄升序排序。

那么相应的 SQL 如下:

select name, age, city from user where city = '北京' order by age limit 5;

这条 SQL 语句逻辑简单清晰,要点有3个:

  • city = ‘北京’ :有where 查询条件
  • order by age :根据 age 排序,默认是asc
  • limit 5 :取得 top5

那么 mysql 底层是如何执行的呢?

首先,给大家介绍一下宏观的思路。

Order by 执行的两步

总体来说,Order by 执行流程,分为两步,具体如下:

第1步:索引的查找

根据where 后面的字段,进行 二级索引的查找,找到后再回表 聚集索引,拿到需要的字段

第2步:原始数据的排序

原始数据的数据, 并不是按照 order by 有序的。

所以,需要按照 order by 字段,进行排序。

接下来,排序的地点在哪里呢?

  • 优先选择内存。因为内存的性能高。
  • 如果原始的数据实在规模太大,就借助磁盘进行排序。

用于排序的内存,称为 sort_buffer。其实 MySQL 会给每个线程分配一块内存用于排序的 sort_buffer

了解了整个步骤后,开始来看看执行计划。

然后再看怎么优化。

Explain查看执行计划

我们先用Explain关键字查看一下执行计划。

那么相应的 SQL 如下:

select name, age, city from user where city = '北京' order by age limit 5;

可以看到:

  • key 字段表示使用到 city 索引,
  • Extra 字段中的Using index condition 表示用到了索引条件,
  • Using filesort表示用到了文件排序 。

用到文件排序,说明第一次查出来的 原始数据,在内存放不下, 需要借助 磁盘空间进行排序,

磁盘IO的性能比较低的,所以,需要进行调优。

再调优之前,首先图解一下order

图解一下Order by 执行的两步

第1步:索引的查找

根据where 后面的字段,进行 二级索引的查找,找到后再回表 聚集索引,拿到需要的字段

回顾 SQL 如下:

select name, age, city from user where city = '北京' order by age limit 5;

首先从 二级索引city 索引树 的查找 city = ‘北京’ 的索引叶子。

在 city 索引树中是非聚簇索引树,叶子节点存储的是主键 ID。city 索引树 如下:

聚簇索引树的叶子节点则存放的是每行数据,如下图:

查询语句的执行流程就是先通过 city 索引树,找到对应的主键 ID,然后再搜索主键索引树,找到对应的行数据。

这些数据是原始数据,放在内存 sort_buffer 中。

第2步:原始数据的排序

原始数据的数据, 并不是按照 order by 有序的。 所以,需要按照 order by 字段,进行排序。

加上order by排序之后,整体的执行流程就是:

Order by 执行完整流程,如下:

  1. 当前线程首先初始化 sort_buffer 块,
  2. 然后从 city 索引树从查询一条满足city='北京'的主键 ID, 比如图中的id=2,
  3. 接着在聚簇索引树中查询id=2的一行数据,将nameagecity三个字段的值,存到sort_buffer
  4. 继续重复前两个步骤,直到 city 索引树中找不到city='北京'的主键 ID。
  5. 最后在sort_buffer中,将所有数据根据age 进行排序,取前 5 行返回给客户端。

全字段排序就是将查询所需的字段,如nameagecity三个字段数据全部存到 sort_buffer 中。

3个核心概念

接下来开始给面试官介绍如何调优。

不过不急,调优涉及到3个核心概念

  • 全字段排序
  • 外部排序
  • rowid 排序

全字段排序

sort_buffer是 MySQL 为每个任务线程维护的一块内存区域,用于进行排序。

sort_buffer 的大小可以通过 sort_buffer_size 来设置。

那这种处理方式会存在一个问题, sort_buffer 是一块固定大小的内存,如果数据量太大,sort_buffer 放不下怎么办呢?

sort_buffer_size 是一个用于控制sort_buffer内存大小的参数。

外部排序

如果要排序的数据小于 sort_buffer_size,那在 sort_buffer 内存中就可以完成排序,如果要排序的数据大于 sort_buffer_size,则需要外部排序,借助磁盘文件来进行排序。

通过执行一下命令,可以查看 SQL 语句执行中是否采用了磁盘文件辅助排序。

set optimizer_trace = "enabled=on";

select name,age,city from user where city = '北京' order by age limit 5;

select * from information_schema.optimizer_trace;

可以从number_of_tmp_files中看出,是否使用了临时文件。

{
    "join_execution": {
      "select#": 1,
      "steps": [
        {
          "filesort_information": [
            {
              "direction": "asc",
              "table": "`user`",
              "field": "age"
            }
          ],
          "filesort_priority_queue_optimization": {
            "limit": 5,
            "rows_estimate": 992,
            "row_size": 112,
            "memory_available": 262144,
            "chosen": true
          },
          "filesort_execution": [
          ],
          "filesort_summary": {
            "rows": 3,
            "examined_rows": 28,
            "number_of_tmp_files": 0,
            "sort_buffer_size": 720,
            "sort_mode": "<sort_key, additional_fields>"
          }
        }
      ]
    }
}

number_of_tmp_files 表示使用来排序的磁盘临时文件数。如果 number_of_tmp_files>0,则表示使用了磁盘文件来进行排序。

使用了磁盘临时文件后,当 sort_buffer 内存不足时,先进行排序,将排序后的数据存放到一个小磁盘文件中,清空 sort_buffer。

然后继续存放数据到 sort_buffer,重复以上步骤。最后将多个磁盘小文件合并成一个有序的大文件。

Tips: 磁盘小文件合并排序,使用的是归并排序算法

这样依然会存在问题,数据存放到临时磁盘小文件,然后还需要归并排序为大文件,再读入到内存中,返回结果集,整体排序效率低下。

rowid 排序

要解决上述问题,可以将只需要用于排序的字段和主键 ID 放入 sort_buffer 中,也就是 rowid 排序,这样就可以在 sort_buffer 中完成排序。

max_length_for_sort_data 是一个用于表示 Mysql 用于排序行数据长度的一个参数,如果单行数据的长度超过了这个值,那么可能会导致采用临时文件排序,mysql 会换用 rowid 排序。

可以通过命令看下这个参数取值。

show variables like 'max_length_for_sort_data';

max_length_for_sort_data 默认值是 1024。本文中name,age,city 长度=64+4+64=132<1024, 所以走的是全字段排序。

我们执行以下命令,改下参数值,再重新执行 SQL。

set max_length_for_sort_data = 32;

select name,age,city from user where city = '北京' order by age limit 5;

使用 rowid 排序的后,执行示意图如下:

对比全字段排序,rowid 排序最后需要根据主键 ID 获取对应字段数据即多了回表查询。

当需要查询的数据在索引树中不存在的时候,需要再次到聚集索引中去获取,这个过程叫做回表

我们通过执行以下命令,可以看到是否使用了 rowid 排序的:

set optimizer_trace = "enabled=on";

select name,age,city from user where city = '北京' order by age limit 5;

select * from information_schema.optimizer_trace;
{
  "join_execution": {
    "select#": 1,
    "steps": [
      {
        "filesort_information": [
          {
            "direction": "asc",
            "table": "`user`",
            "field": "age"
          }
        ],
        "filesort_priority_queue_optimization": {
          "limit": 5,
          "rows_estimate": 992,
          "row_size": 8,
          "memory_available": 262144,
          "chosen": true
        },
        "filesort_execution": [],
        "filesort_summary": {
          "rows": 3,
          "examined_rows": 28,
          "number_of_tmp_files": 0,
          "sort_buffer_size": 96,
          "sort_mode": "<sort_key, rowid>"
        }
      }
    ]
  }
}

sort_mode表示排序模式为 rowid 排序。

order by 的优化思路

重点来了

如何调优呢?两大措施:

  • 联合索引优化

    如果数据本身是有序的,那就不需要排序,而索引数据本身是有序的,所以,我们可以通过建立联合索引,跳过排序步骤。

  • 参数优化

    可以通过调整max_length_for_sort_data等参数优化排序算法;

联合索引

基于查询条件和排序条件,给表加个联合索引idx_city_age。然后再查看执行计划:

alter table user add  index idx_city_age(city,age);

explain select name,age,city from user where city = '北京' order by age limit 5;

从执行计划Extra字段中可以发现没有再使用Using filesort排序,keys使用了idx_city_age索引。

idx_city_age联合索引示意图,如下:

整个 SQL 执行流程图如下所示:

任务线程从idx_city_age索引树中可以有序的获取满足条件的主键 ID, 然后根据主键 ID 查询对应字段数据,不需要在 sort_buffer 中排序了。

从示意图看来,还是有一次回表操作,那可以通过覆盖索引来解决,给citynameage 组成一个联合索引,省去回表操作。

覆盖索引是 select 的数据列只用从索引中就能够取得,不必读取数据行,换句话说查询列要被所建的索引覆盖。

参数优化

通过调整参数,也可以优化order by的执行。

  1. 调整 sort_buffer_size 参数的值。如果 sort_buffer 值太小而数据量大的话,MySQL 会采用磁盘临时文件辅助排序。MySQL 服务器配置高的情况下,可以将参数调大些。
  2. 调整 max_length_for_sort_data 的值,值太小的话 MySQL 会采用 rowid 排序,会多一次回表操作导致查询性能降低。同样可以适当调大些。

推荐阅读:

《网易二面:CPU狂飙900%,该怎么处理?》

《阿里二面:千万级、亿级数据,如何性能优化? 教科书级 答案来了》

《峰值21WQps、亿级DAU,小游戏《羊了个羊》是怎么架构的?》

《场景题:假设10W人突访,你的系统如何做到不 雪崩?》

《2个大厂 100亿级 超大流量 红包 架构方案》

《Nginx面试题(史上最全 + 持续更新)》

《K8S面试题(史上最全 + 持续更新)》

《操作系统面试题(史上最全、持续更新)》

《Docker面试题(史上最全 + 持续更新)》

《Springcloud gateway 底层原理、核心实战 (史上最全)》

《Flux、Mono、Reactor 实战(史上最全)》

《sentinel (史上最全)》

《Nacos (史上最全)》

《TCP协议详解 (史上最全)》

《分库分表 Sharding-JDBC 底层原理、核心实战(史上最全)》

《clickhouse 超底层原理 + 高可用实操 (史上最全)》

《nacos高可用(图解+秒懂+史上最全)》

《队列之王: Disruptor 原理、架构、源码 一文穿透》

《环形队列、 条带环形队列 Striped-RingBuffer (史上最全)》

《一文搞定:SpringBoot、SLF4j、Log4j、Logback、Netty之间混乱关系(史上最全)》

《单例模式(史上最全)》

《红黑树( 图解 + 秒懂 + 史上最全)》

《分布式事务 (秒懂)》

《缓存之王:Caffeine 源码、架构、原理(史上最全,10W字 超级长文)》

《缓存之王:Caffeine 的使用(史上最全)》

《Java Agent 探针、字节码增强 ByteBuddy(史上最全)》

《Docker原理(图解+秒懂+史上最全)》

《Redis分布式锁(图解 - 秒懂 - 史上最全)》

《Zookeeper 分布式锁 - 图解 - 秒懂》

《Zookeeper Curator 事件监听 - 10分钟看懂》

《Netty 粘包 拆包 | 史上最全解读》

《Netty 100万级高并发服务器配置》

《Springcloud 高并发 配置 (一文全懂)》## 推荐阅读:

《网易二面:CPU狂飙900%,该怎么处理?》

《阿里二面:千万级、亿级数据,如何性能优化? 教科书级 答案来了》

《峰值21WQps、亿级DAU,小游戏《羊了个羊》是怎么架构的?》

《场景题:假设10W人突访,你的系统如何做到不 雪崩?》

《2个大厂 100亿级 超大流量 红包 架构方案》

《Nginx面试题(史上最全 + 持续更新)》

《K8S面试题(史上最全 + 持续更新)》

《操作系统面试题(史上最全、持续更新)》

《Docker面试题(史上最全 + 持续更新)》

《Springcloud gateway 底层原理、核心实战 (史上最全)》

《Flux、Mono、Reactor 实战(史上最全)》

《sentinel (史上最全)》

《Nacos (史上最全)》

《TCP协议详解 (史上最全)》

《分库分表 Sharding-JDBC 底层原理、核心实战(史上最全)》

《clickhouse 超底层原理 + 高可用实操 (史上最全)》

《nacos高可用(图解+秒懂+史上最全)》

《队列之王: Disruptor 原理、架构、源码 一文穿透》

《环形队列、 条带环形队列 Striped-RingBuffer (史上最全)》

《一文搞定:SpringBoot、SLF4j、Log4j、Logback、Netty之间混乱关系(史上最全)》

《单例模式(史上最全)》

《红黑树( 图解 + 秒懂 + 史上最全)》

《分布式事务 (秒懂)》

《缓存之王:Caffeine 源码、架构、原理(史上最全,10W字 超级长文)》

《缓存之王:Caffeine 的使用(史上最全)》

《Java Agent 探针、字节码增强 ByteBuddy(史上最全)》

《Docker原理(图解+秒懂+史上最全)》

《Redis分布式锁(图解 - 秒懂 - 史上最全)》

《Zookeeper 分布式锁 - 图解 - 秒懂》

《Zookeeper Curator 事件监听 - 10分钟看懂》

《Netty 粘包 拆包 | 史上最全解读》

《Netty 100万级高并发服务器配置》

《Springcloud 高并发 配置 (一文全懂)》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/334551.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机视觉 吴恩达 week 10 卷积

文章目录一、边缘检测二、填充 padding1、valid convolution2、same convolution三、卷积步长 strided convolution四、三维卷积五、池化层 pooling六、 为什么要使用卷积神经网络一、边缘检测 可以通过卷积操作来进行 原图像 n✖n 卷积核 f✖f 则输出的图像为 n-f1 二、填充…

【软考】系统集成项目管理工程师(二十)项目风险管理

一、项目风险管理概述1. 风险概念2. 风险分类3. 风险成本二、项目风险管理子过程1. 规划风险管理2. 识别风险3. 实施定性风险分析4. 实施定量风险分析5. 规划风险应对6. 控制风险三、项目风险管理流程梳理一、项目风险管理概述 1. 风险概念 风险是一种不确定事件或条件,一旦…

java赫夫曼编码

1.基本介绍 赫夫曼编码也翻译为 哈夫曼编码(Huffman Coding)&#xff0c;又称霍夫曼编码&#xff0c;是一种编码方式, 属于一种程序算法赫夫曼编码是赫哈夫曼树在电讯通信中的经典的应用之一。赫夫曼编码广泛地用于数据文件压缩。其压缩率通常在 20%&#xff5e;90%之间赫夫曼…

[Android]网络框架之Retrofit(kotlin)

目录 Retrofit简介 Retrofit基本使用 Retrofit的注解 Retrofit的转换器 文件的上传与下载 Retrofit简介 Retrofit是一款由Square公司开发的网络库&#xff0c;但是它和OkHttp的定位完全不同。 OkHttp侧重的是底层通信的实现&#xff0c;而Retrofit侧重的是上层接口的封装…

永磁同步电机中BEMF电阻的作用

一、电路原理图 二、原理分析 如图一我们测的是相电压&#xff0c;从理论上我们知道我们测得相电压是一个马鞍波形&#xff0c;马鞍波形中并没有隐含 转子的位置和速度信息。那么为什么我们还要有这样一个电路呢&#xff1f; 这个问题其实困惑了我好久&#xff1f;直到有一天…

曹云金对德云社最大的贡献,就是促进了薪酬体系改革

虽然曹云金已经离开德云社&#xff0c;但是关于他和德云社的话题&#xff0c;却从来没有间断过&#xff0c;尤其是他和小岳岳的对比&#xff0c;更是很有争议的一个话题。实话实说&#xff0c;曹云金在德云社的这些年&#xff0c;对这个这个民间相声社团发展&#xff0c;还是做…

Docker实战

目录一、FROM 语法二、label语法三、run语法四、workdir 语法五、add 和copy 语法六、ENV语法七、volume 和expose 语法八、run、cmd 和entrypoint一、FROM 语法scratch -- 从头开始尽量来使用官方提供的imageFROM 指定基础镜像&#xff0c;最好挑一些apline&#xff0c;slim之…

Qml学习——控件状态

最近在学习Qml&#xff0c;但对Qml的各种用法都不太熟悉&#xff0c;总是会搞忘&#xff0c;所以写几篇文章对学习过程中的遇到的东西做一个记录。 学习参考视频&#xff1a;https://www.bilibili.com/video/BV1Ay4y1W7xd?p1&vd_source0b527ff208c63f0b1150450fd7023fd8 其…

Apache安全加固配置教程(小白篇)

Apache安全加固配置教程(小白篇) 资源宝分享&#xff1a;www.httple.net 一&#xff0c;Apache服务器的介绍 Apache服务器它是Internet网上应用最为广泛的Web服务器软件之一。Apache服务器源自美国国家超级技术计算应用中心&#xff08;NCSA&#xff09;的 Web服务器项目中。目…

SAS应用入门学习笔记3

操作数据集的观测&#xff1a; Eg. 修改变量值等 变量的值取出来&#xff0c;那么我们需要对变量的值进行修改 weight height bmi? Missing 用到条件语if then、赋值语句、表达式 等。 表达式是操作数和操作符的序列。 例如&#xff1a;3 x x1 1、操作数&#xff1a;…

每日学术速递2.9

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV、cs.AI、cs.LG、cs.IR 1.Graph Signal Sampling for Inductive One-Bit Matrix Completion: a Closed-form Solution(ICLR 2023) 标题&#xff1a;归纳单比特矩阵完成的图信号采样&am…

程序员该不该在年后跳槽?3个问句给你答案

过完春节&#xff0c;2023年也迎来了第二个月份&#xff0c;有些程序员虽然还在公司上班&#xff0c;但是私底下跳槽的心蠢蠢欲动&#xff0c;简历说不定都改到第三版了。 在各大社交平台上&#xff0c;经常能看到不少程序员分享自己的跳槽经历&#xff0c;尤其是春节后更是如…

13薪|运营策划[北京市 - 海淀区]-10k-15k

"众推职聘”以交付结果为宗旨的全流程化招聘服务平台&#xff01;今日招聘信息↓【工作内容】1、根据项目要求&#xff0c;收集相关数据&#xff0c;策划撰写项目运营方案&#xff1b;2、运营合作环节中&#xff0c;监督管理执行&#xff1b;3、参与项目的评估&#xff1b…

WorkTool无障碍服务实现企业微信机器人接口

前言 想要实现一个企业微信机器人&#xff0c;如京东/拼多多福利群、美团瑞幸定时营销群、自助订单查询、智能咨询或社群管理机器人等&#xff0c;首先官方未提供外部群/客户群的机器人API&#xff0c;会话存档也只在一定场景下适用&#xff0c;及时使用会话存档也存在只能收不…

56 门控循环单元(GRU)【动手学深度学习v2】

56 门控循环单元&#xff08;GRU&#xff09;【动手学深度学习v2】 深度学习学习笔记 学习视频&#xff1a;https://www.bilibili.com/video/BV1mf4y157N2/?spm_id_fromautoNext&vd_source75dce036dc8244310435eaf03de4e330 门控循环单元GRU GRU和LSTM 实际上效果差不多。…

030_SSS_MaskSketch Unpaired Structure-guided Masked Image Generation

MaskSketch: Unpaired Structure-guided Masked Image Generation 1. Introduction 本文在MaskGIT的基础上进行了改进&#xff0c;提出了MaskSketch用于sketch-to-photo。MaskSketch直接使用预训练好的MaskGIT&#xff0c;不需要进行模型的训练&#xff0c;而且不需要成对的监…

29 - 面向对象的三大特征 - 多态

目录 一、理解多态 1、概念 2、使用场景 3、特点 二、多态案例1 1、需求 2、代码实现 一、理解多态 1、概念 不同的子类对象调用相同的父类方法&#xff0c;产生不同的执行结果2、使用场景 以继承和重写父类方法为前提是调用方法的技巧&#xff0c;不会影响到类的内部设计3、特…

Kubernetes那点事儿——健康检查

K8s应用程序生命周期管理——健康检查前言一、重启策略二、健康检查三、健康监控方式前言 官网&#xff1a;https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-readiness-probes 一、重启策略 Always&a…

云原生技术在容器方面的应用

随着近几年云原生生态的不断壮大&#xff0c;众多企业纷纷开展了用云上云的工作&#xff0c;学习和了解云原生及容器技术对于现代工程师是必不可少的&#xff0c;本文主要为大家介绍云原生及其在容器方面的应用。 1.1 什么是云原生 云原生技术有利于各组织在公有云、私有云和…

基于可视化 BI 工具 DataEase 制作第七次人口普查数据分析大屏

一、制作第七次人口普查数据大屏的背景介绍&#xff1a;第七次人口普查刚刚结束&#xff0c;想要制作一个大屏&#xff0c;能够直观的看到人口总数、出生人数、死亡人数等的情况&#xff1b;希望能够直观的看到人口分布的情况、各种比例、年龄结构等有一定的了解&#xff1b;直…