首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >MySQL性能优化 - 索引匹配 - 最左前缀

MySQL性能优化 - 索引匹配 - 最左前缀

原创
作者头像
爱我所爱
修改于 2024-11-26 12:42:49
修改于 2024-11-26 12:42:49
2500
举报
文章被收录于专栏:MySQLMySQL

今日推荐: Kubernetes 初学者教程:47 个综合指南(持续完善中)

文章链接: https://cloud.tencent.com/developer/article/2465090

推荐语: 持续不断地学习是程序员的必备技能,而详尽的指南带我们一步一步走向成功,该指南对于学习Kubernate提供了完备的路径和文章目录,相信耐心的跟着作者学完所有课程,则对于该运维工具的使用一定能达到一定的高度。

下面开始今天对于索引匹配最左前缀的介绍

很多因素都会影响MySQL的性能,但是索引的特别之处在于没有索引的话我们的性能目标是无论如何也不可能达到的。

Explain语句可以帮我们验证某个查询是否用到索引以及用的是哪一个索引,但是我们不能漫无目的的建立索引。

所以得先知道索引的一些匹配原则,即在哪些情况下索引可能会生效,哪些情况下索引无法生效,leftmost prefix(最左前缀)

就是索引匹配其中的一个原则, 接下来将结合实践演示该原则的使用。

还是以employees数据库中的employees表为例,如果我们为该表建立了索引idx_name(last_name, first_name)

代码语言:sql
AI代码解释
复制
mysql> create index idx_name on employees(last_name, first_name);
Query OK, 0 rows affected (1.29 sec)
Records: 0  Duplicates: 0  Warnings: 0

那么我们在执行以下查询时都可以用到该索引

代码语言:sql
AI代码解释
复制
mysql> explain select * from employees where last_name = "Facello" \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: employees
   partitions: NULL
         type: ref
possible_keys: idx_name
          key: idx_name
      key_len: 66
          ref: const
         rows: 186
     filtered: 100.00
        Extra: NULL
1 row in set, 1 warning (0.01 sec)

从key 可以看到用到idx_name这个索引,匹配到的行是186行

代码语言:sql
AI代码解释
复制
mysql> explain select * from employees where last_name = "Facello" and first_name = "Georgi" \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: employees
   partitions: NULL
         type: ref
possible_keys: idx_name
          key: idx_name
      key_len: 124
          ref: const,const
         rows: 2
     filtered: 100.00
        Extra: NULL
1 row in set, 1 warning (0.00 sec)

从key可以看到用到了idx_name这个索引,匹配到的行是2行

代码语言:sql
AI代码解释
复制
mysql> explain select * from employees where last_name = "Simmel" and (first_name = "Xuejun" or first_name = "Tokuyasu") \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: employees
   partitions: NULL
         type: range
possible_keys: idx_name
          key: idx_name
      key_len: 124
          ref: NULL
         rows: 4
     filtered: 100.00
        Extra: Using index condition
1 row in set, 1 warning (0.00 sec)

从key可以看到用到了idx_name这个索引,匹配到的行是4行

代码语言:sql
AI代码解释
复制
mysql> explain select * from employees where last_name = "Simmel" and first_name in ("Xuejun", "Tokuyasu") \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: employees
   partitions: NULL
         type: range
possible_keys: idx_name
          key: idx_name
      key_len: 124
          ref: NULL
         rows: 4
     filtered: 100.00
        Extra: Using index condition
1 row in set, 1 warning (0.01 sec)

从key可以看到用到了idx_name这个索引,匹配到的行是4行

以上查询都是last_name在前, 没有用到first_name或者first_name在后,和我们建索引时候的列顺序是最左前缀匹配的,所以能用到该索引。

代码语言:sql
AI代码解释
复制
mysql> explain select * from employees where first_name = "Georgi" and last_name = "Facello" \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: employees
   partitions: NULL
         type: ref
possible_keys: idx_name
          key: idx_name
      key_len: 124
          ref: const,const
         rows: 2
     filtered: 100.00
        Extra: NULL
1 row in set, 1 warning (0.00 sec)

从key可以看到用到了idx_name这个索引,匹配到的同样的2行,这是因为MySQL查询优化器在执行查询之前会重写 SQL 语句,所以实际执行的是select * from employees where last_name = "Facello" and first_name = "Georgi", 和前一条语句一样。

代码语言:sql
AI代码解释
复制
mysql> explain select * from employees where last_name = "Simmel" and emp_no = 295735 \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: employees
   partitions: NULL
         type: const
possible_keys: PRIMARY,idx_name
          key: PRIMARY
      key_len: 4
          ref: const
         rows: 1
     filtered: 100.00
        Extra: NULL
1 row in set, 1 warning (0.00 sec)

从possible_keys和keys可以看到可能用到的key是idx_name和PRIMARY, 但是因为PRIMARY匹配到的行更少,所以在实际执行中会选用PRIMARY这个索引。

但是对于以下的查询,就无法使用索引了

代码语言:sql
AI代码解释
复制
mysql> explain select * from employees where first_name = "Georgi" \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: employees
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 297793
     filtered: 10.00
        Extra: Using where
1 row in set, 1 warning (0.00 sec)

从possible_keys 和keys可以看到该查询没有使用索引, type为ALL表明MySQL将遍历全表来找到所需要的行。这是因为该查询值包含first_name这一列,实际上index是从last_name开始的,所以无法用到。

我们来看各个查询的实际用时

代码语言:sql
AI代码解释
复制
| 0.00302800 | select * from employees where last_name = "Facello"                                                       |
| 0.00033100 | select * from employees where last_name = "Facello" and first_name = "Georgi"                             |
| 0.00208400 | select * from employees where last_name = "Simmel" and (first_name = "Xuejun" or first_name = "Tokuyasu") |
| 0.00061400 | select * from employees where last_name = "Simmel" and first_name in ("Xuejun", "Tokuyasu")               |
| 0.00058100 | select * from employees where first_name = "Georgi" and last_name = "Facello"                             |
| 0.00038700 | select * from employees where last_name = "Simmel" and emp_no = 295735                                    |
| 0.14507000 | select * from employees where first_name = "Georgi"  

如果我们用如下的三列的索引

代码语言:sql
AI代码解释
复制
mysql> create index indx_name_gender on employees(last_name,first_name,gender);
Query OK, 0 rows affected (1.21 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> drop index idx_name on employees;
Query OK, 0 rows affected (0.02 sec)
Records: 0  Duplicates: 0  Warnings: 0

我们可以看到在没有last_name的情况下都不能用到索引

代码语言:sql
AI代码解释
复制
mysql> explain select * from employees where first_name = "Xuejun" and  gender = "M" \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: employees
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 297793
     filtered: 5.00
        Extra: Using where
1 row in set, 1 warning (0.00 sec)
代码语言:sql
AI代码解释
复制
mysql> explain select * from employees where gender = "M"  and first_name = "Xuejun"\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: employees
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 297793
     filtered: 5.00
        Extra: Using where
1 row in set, 1 warning (0.00 sec)

虽然first_name和gender都在idx_name_gender中,但是因为都不是索引中的最左列,所以在只有它们自己时, 用不到索引。

但是从以下的查询中可以看到只要加上last_name就可以用到索引了

代码语言:sql
AI代码解释
复制
mysql> explain select * from employees where gender = "M"  and first_name = "Xuejun" and last_name = "Simmel" \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: employees
   partitions: NULL
         type: ref
possible_keys: indx_name_gender
          key: indx_name_gender
      key_len: 125
          ref: const,const,const
         rows: 1
     filtered: 100.00
        Extra: Using index condition
1 row in set, 1 warning (0.00 sec)

实际看下各个查询的用时

代码语言:sql
AI代码解释
复制

| 0.00034900 | select * from employees where gender = "M"  and first_name = "Xuejun" and last_name = "Simmel"         |
| 0.16502500 | select * from employees where gender = "M"  and first_name = "Xuejun"                                  |
| 0.17797900 | select * from employees where  first_name = "Xuejun" and gender = "M"    |

从以上两个profling的数据中可以看出:

  1. 使用PRIMARY索引的用时是最少的,因为PRIMARY使用的是聚簇索引(一旦找到索引中的值,就能直接获取到对应的数据行)
  2. 用到非主键索引的其次(非主键索引会存储对应的主键值,在找到索引后会进行回表找到主键值对应的行)
  3. 而没有用到索引的查询比用到索引的慢很多(需遍历全表来找到所需要的行)

所以我们应该根据实际查询的用到最多的列来设置索引,如果是在多列索引的情况下,最常用的列放在索引定义最前面,并在查询中尽量使用PRIMARY索引。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
gtest单元测试框架介绍及简单使用
Gtest是Google的一个开源框架,它主要用于写单元测试,检查真自己的程序是否符合预期行为。可在多个平台上使用(包括Linux, Mac OS X, Windows, Cygwin和Symbian)。它提供了丰富的断言、致命和非致命失败判断,能进行值参数化测试、类型参数化测试、“死亡测试”。
杨永贞
2022/04/13
4.4K0
gtest单元测试框架介绍及简单使用
纯血鸿蒙APP实战开发——预加载so并读取RawFile文件
本示例主要介绍在TaskPool子线程中使用 dlopen 预加载 so 库并使用句柄调用库函数的方法,以及在Native中使用 pread 系统函数读取Rawfile文件的部分文本内容,并添加 HiLog 日志。
小帅聊鸿蒙
2025/01/21
2960
纯血鸿蒙APP实战开发——预加载so并读取RawFile文件
lycium上面适配OpenHarmony 不同架构的构建
当前lycium上面支持armv7-a和armv8-a的构建,其他架构的构建也是类似的,在HPKBUILD文件中,需要判断架构的地方加上架构相关的信息,这里以新增x86_64架构举例,cmake,makefile, configure构建方式如何修改。
小帅聊鸿蒙
2024/08/28
2210
lycium上面适配OpenHarmony 不同架构的构建
4步成功将三方库——speexdsp移植到OpenHarmony
①在原生库根目录中,存在CMakeLists.txt文件。在原生库下新建build目录通过cmake或者cmake-gui生成makeflie然后make。
小帅聊鸿蒙
2025/04/20
2200
4步成功将三方库——speexdsp移植到OpenHarmony
QT的cmake项目工程配置
QT的项目工程默认使用的是qmake,这本来也没什么问题。但是由于要用到vcpkg这个工具来管理第三方库,好像这个 vcpkg工具只能在cmake的项目中才能使用。
杨永贞
2022/04/13
3.8K0
Android NDK 开发 | CMake 使用手册 - 初见篇
之前写过一篇 《[-NDK 导引篇 -] 在NDK开发之前你应知道的东西》 介绍了在进入 NDK 学习之前,如何摆正自己的角色。时隔两年,NDK 系列文章开始填坑,在上一篇 《 NDK 是什么 | FFmpeg 5.0 编译 so 库》 中,介绍了 NDK 的概念,以及其作用。
张风捷特烈
2022/09/20
1.5K0
Android NDK 开发 | CMake 使用手册 - 初见篇
OpenHarmony开发——CMake方式组织编译的库移植
本文为OpenHarmony开发者提供一些组织编译形式比较常见(CMakeLists、Makefile)的三方库的移植指南,该指南当前仅适用于Hi3516DV300和Hi3518EV300两个平台,文中着重介绍各编译组织方式下工具链的设置方法以及如何将该库的编译添加到OpenHarmony整个工程的构建中。
小帅聊鸿蒙
2024/08/06
5850
OpenHarmony开发——CMake方式组织编译的库移植
HarmonyOS 开发实践 —— 基于lycium的开源c库编译与集成
lycium的使用说明:lycium的特点就是自动化编译,只需要一个./build.sh命令就可以完成编译,下面的编译例子(openssl)代码库是通过hpkbuild脚本从github或者gitee上面拉取,在hpkbuild脚本中已经默认提供了一套编译默认的配置,也可以按照需求修改。
小帅聊鸿蒙
2024/11/23
4030
HarmonyOS 开发实践 —— 基于lycium的开源c库编译与集成
[-NDK 导引篇 -] 在NDK开发之前你应知道的东西
前言 笔者看了一些NDK的项目。一些教程不是HelloWord就是直接整FFmpeg或OpenCV,可谓一个天一个地,而且目录结构和Android3.5的默认结构并不是太一致,一直没找到什么合心的文章。故写此文连接这天地,来总结一下在NDK开发之前你应知道的东西。 ---- 在此之前,先划分三类人,如果不认清自己是什么角色(垃圾)就去玩NDK,你会很糟心: user : 纯粹.so链接库使用者(伸手党) creator : 纯粹ndk开发者,创作.so链接库(创作家) designer : 在现
张风捷特烈
2020/04/30
7790
[-NDK 导引篇 -] 在NDK开发之前你应知道的东西
HarmonyOS 开发实践——对各种图片编解码格式的支持
在lycium目录下会生成usr目录,该目录下存在已编译完成的32位和64位三方库和头文件
小帅聊鸿蒙
2024/11/12
2260
HarmonyOS 开发实践——对各种图片编解码格式的支持
GTest问题_itest.n z
添加以下代码,注意==不要包含gtest/gtest.h头文件==,若多包含可能会出现struct std::__cxx11::basic_stringbuf<_CharT, _Traits, _Alloc>::__xfer_bufptrs' redeclared with different access错误
全栈程序员站长
2022/11/10
7680
C++与lua的结合,LuaBridge的使用
LuaBridge 是一个简单好用的轻量级且无依赖的库,用于在C ++和 LUA(一种强大,快速,轻量级,可嵌入的脚本语言)之间来回映射数据,函数和类。
杨永贞
2022/05/11
1.9K0
C++与lua的结合,LuaBridge的使用
编译Android 使用的 libx264 并使用进行 H.264 编码
在日常的音视频开发中,我们经常使用FFmpeg,因为它确实好用呀,囊括了各种功能!但是有个很严重的问题,如果是编译在Android和IOS上使用,会造成APP的包很大。可以看我编译的FFmpeg在Android上的应用程式。
字节流动
2021/06/09
5K0
编译Android 使用的 libx264 并使用进行 H.264 编码
ArkUI实战开发-NAPI项目
上节笔者简单介绍了使用 DevEco Studio 创建的默认 NAPI 工程结构,本节笔者简单介绍一下 NAPI 工程下 cpp 目录的源码部分。
小帅聊鸿蒙
2024/10/09
1940
ArkUI实战开发-NAPI项目
NDK 是什么 | FFmpeg 5.0 编译 so 库
NDK 全称 Native Development Kit,也就是原生开发工具包 ,官网对它有详细的 中文介绍 。可能一说到 NDK 或 JNI ,大家脑子里第一反应就是集成 C/C++ 。其实 JNI 的含义是 Java Native Interface ,这种接口允许 Java 和其他语言进行交互的,包括但不限于 C/C++ 。目前 Rust 也可以通过 JNI 来和 Java 交互,虽然不太成熟。
张风捷特烈
2022/09/20
1.6K0
NDK 是什么 |  FFmpeg 5.0 编译 so 库
OpenHarmony 内核源码分析(gn应用篇) | gn语法及在鸿蒙的使用
gn 存在的意义是为了生成 ninja,如果熟悉前端开发,二者关系很像 Sass和CSS的关系.
小帅聊鸿蒙
2025/03/26
2810
算法部署 | 万字长文带你从C++案例一步一步实操cmake(起飞系列)
你或许听过好几种Make工具,例如GNU Make ,QT的qmake ,微软的MS nmake,BSD Make(pmake),Makepp,等等。这些Make工具遵循着不同的规范和标准,所执行的Makefile格式也千差万别。这样就带来了一个严峻的问题:如果软件想跨平台,必须要保证能够在不同平台编译。而如果使用上面的 Make 工具,就得为每一种标准写一次Makefile,这将是一件让人抓狂的工作。
集智书童公众号
2021/07/30
1.6K0
Android JNI 开发
几乎稍有经验的Android开发,都会在工作中用到JNI的开发。即使工作中没有涉及到JNI的开发,在我们使用第三方的库时,也经常需要引入.so文件。
Oceanlong
2019/02/22
1.4K0
Android JNI 开发
Flutter鸿蒙终端一体化-混沌初开
flutter_flutter项目,这是我们整个工程的核心,但是很多人都倒在了第一步,其原因,就是——需要使用Dev分支!!! https://gitee.com/openharmony-sig/flutter_flutter/tree/dev/
用户1907613
2024/01/24
3.1K0
Flutter鸿蒙终端一体化-混沌初开
使用京东Taro开发鸿蒙PC(附鸿蒙版Taro环境搭建流程)
打开 DevEco,点击右上角的 Create Project,在 Application 处选择 Empty Ability,点击 Next,进入配置页面,根据需求调整内容,这里使用默认配置:
徐建国
2025/05/21
5330
使用京东Taro开发鸿蒙PC(附鸿蒙版Taro环境搭建流程)
推荐阅读
相关推荐
gtest单元测试框架介绍及简单使用
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档