MySQL的索引相关

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用系列 2核4GB
简介: MySQL 把数据存储和查询操作抽象成了存储引擎,不同的存储引擎,对数据的存储和读取方式各不相同。MySQL 支持多种存储引擎,并且可以以表为粒度设置存储引擎。因为支持事务,我们最常使用的是 InnoDB。

MySQL的索引相关

InnoDB如何存储数据的

MySQL 把数据存储和查询操作抽象成了存储引擎,不同的存储引擎,对数据的存储和读取方式各不相同。MySQL 支持多种存储引擎,并且可以以表为粒度设置存储引擎。因为支持事务,我们最常使用的是 InnoDB。

那么InnoDB 是如何存储数据的?虽然数据保存在磁盘中,但其处理是在内存中进行的。为了减少磁盘随机读取次数,InnoDB 采用页而不是行的粒度来保存数据,即数据被分成若干页,以页为单位保存在磁盘中。InnoDB 的页大小,一般是 16KB。

数据页是InnoDB中的基本存储单元,用于存储表的数据行和索引节点。每个数据页由文件头、页头、Infimum和Supremum记录、用户记录、空闲空间和页目录组成。

数据页的基本结构可以分为以下几个部分:

  • 文件头(File Header): 文件头保存了数据页的一些基本信息,比如页号、页类型等。
  • 页头(Page Header): 页头包含了数据页的管理信息,比如页面大小、空闲空间大小等。
  • Infimum + Supremum Records: 这是InnoDB中特殊的记录。Infimum表示虚拟的最小记录,Supremum表示虚拟的最大记录。它们分别位于数据页的最底部和最顶部,用于指向数据页中数据行的起始和结束位置。
  • 用户记录(User Records): 用户记录是实际存储在数据页中的数据行。每个数据页可以存储多个数据行。
  • 空闲空间(Free Space): 空闲空间用于存储新增数据行或已删除数据行的内容。
  • 页目录(Page Directory): 页目录记录了数据页中数据行的位置和大小。通过页目录可以快速定位数据行。

数据页组成一个双向链表,每个数据页中的记录按照主键顺序组成单向链表;每一个数据页中有一个页目录,方便按照主键查询记录。数据页的结构如下:

1302b4a8d877609486c9a9eed2d8d8d1

页目录通过槽把记录分成不同的小组,每个小组有若干条记录。如图所示,记录中最前面的小方块中的数字,代表的是当前分组的记录条数,最小和最大的槽指向 2 个特殊的伪记录。有了槽之后,我们按照主键搜索页中记录时,就可以采用二分法快速搜索,无需从最小记录开始遍历整个页中的记录链表。

这个结构有没有很熟悉的感觉,是不是很像跳表的数据结构来查找元素的情景。

以上就是InnoDB的底层存储数据的原理

聚簇索引

说到索引,页目录就是最简单的索引,是通过对记录进行一级分组来降低搜索的时间复杂度。

但,这样能够降低的时间复杂度数量级,非常有限。当有无数个数据页来存储表数据的时候,我们就需要考虑如何建立合适的索引,才能方便定位记录所在的页。

为了解决这个问题,InnoDB 引入了 B+ 树。如下图所示,B+ 树是一棵倒过来的树:

e76adf029e63a045e05956039f81f265

B+ 树的特点包括:

  • 最底层的节点叫作叶子节点,用来存放数据;
  • 其他上层节点叫作非叶子节点,仅用来存放目录项,作为索引;
  • 非叶子节点分为不同层次,通过分层来降低每一层的搜索量;
  • 所有节点按照索引键大小排序,构成一个双向链表,加速范围查找。

因此,InnoDB 使用 B+ 树,既可以保存实际数据,也可以加速数据搜索,这就是聚簇索引。

如果把上图叶子节点下面方块中的省略号看作实际数据的话,那么它就是聚簇索引的示意图。

由于数据在物理上只会保存一份,所以包含实际数据的聚簇索引只能有一个。

InnoDB 会自动使用主键(唯一定义一条记录的单个或多个字段)作为聚簇索引的索引键(如果没有主键,就选择第一个不包含 NULL 值的唯一列)。上图方框中的数字代表了索引键的值,对聚簇索引而言一般就是主键。

以下是聚簇索引的一些重要特点:

  1. 物理存储顺序: 聚簇索引中的数据行按照索引键值的顺序在存储介质上进行物理存储,具有相近键值的数据行在物理存储上也是相邻的。
  2. 覆盖索引: 聚簇索引包含了所有的表数据,因此,如果查询只需要聚簇索引中包含的列,而不需要访问表的其他列,那么查询就可以通过聚簇索引进行覆盖索引扫描,避免了额外的表访问,提高了查询性能。
  3. 聚簇和非聚簇索引的选择: 聚簇索引对于范围查询非常高效,但是对于频繁的插入和更新操作,由于需要调整数据行的物理存储位置,可能会导致性能下降。在这种情况下,选择合适的非聚簇索引可能更有利于性能优化。
  4. 数据页分裂: 当一个数据页已满,再插入新的数据行时,InnoDB需要将数据页进行分裂,以便为新的数据行腾出空间。数据页分裂可能会导致数据行的物理存储位置发生变化,影响插入操作的性能。
  5. 聚簇索引的选择: 在设计数据库时,需要根据查询的特点、数据访问模式和性能需求来选择是否使用聚簇索引以及选择哪些列作为聚簇索引。

二级索引

为了实现非主键字段的快速搜索,就引出了二级索引,也叫作非聚簇索引、辅助索引。二级索引,也是利用的 B+ 树的数据结构,如下图所示:

4be8f22d993bd92878209d00a1264b3a

这次二级索引的叶子节点中保存的不是实际数据,而是主键,获得主键值后去聚簇索引中获得数据行。这个过程就叫作回表。

举个例子,有个索引是针对用户名字段创建的,索引记录上面方块中的字母是用户名,按照顺序形成链表。如果我们要搜索用户名为 b 的数据,经过两次定位可以得出在 #5 数据页中,查出所有的主键为 7 和 6,再拿着这两个主键继续使用聚簇索引进行两次回表得到完整数据。

以下是非聚簇索引的一些重要特点:

  1. 数据和索引分离: 非聚簇索引将索引和数据行分开存储。索引本身保存在B+树结构中,每个节点包含键值和指向对应数据行的指针或引用。
  2. 物理存储顺序与索引无关: 与聚簇索引不同,非聚簇索引中的键值不决定数据行的物理存储顺序。数据行在存储介质上可能是随机分布的,这意味着通过非聚簇索引查找数据行时,可能需要进行额外的随机I/O操作。
  3. 覆盖索引: 非聚簇索引中除了包含索引列的键值外,还包含了指向对应数据行的引用。因此,如果查询只需要索引列的数据,而不需要访问表的其他列,那么查询可以通过非聚簇索引进行覆盖索引扫描,避免了额外的表访问,提高了查询性能。
  4. 辅助查询: 非聚簇索引能够加速多种类型的查询操作,比如等值查询、范围查询、排序和分组等。通过合理的非聚簇索引设计,可以优化数据库的查询性能。
  5. 数据页不分裂: 由于非聚簇索引不影响数据行的物理存储顺序,插入和更新数据不会导致数据页的分裂,因此,相较于聚簇索引,非聚簇索引对于插入和更新操作更友好。

索引的代价

创建二级索引的代价,主要表现在维护代价、空间代价和回表代价三个方面。

  • 首先是维护代价。创建 N 个二级索引,就需要再创建 N 棵 B+ 树,新增数据时不仅要修改聚簇索引,还需要修改这 N 个二级索引。
  • 其次是空间代价。虽然二级索引不保存原始数据,但要保存索引列的数据,所以会占用更多的空间。
  • 最后是回表的代价。二级索引不保存原始数据,通过索引找到主键后需要再查询聚簇索引,才能得到我们要的数据。

索引覆盖

如果我们需要查询的是索引列索引或联合索引能覆盖的数据,那么查询索引本身已经“覆盖”了需要的数据,不再需要回表查询。

最后,我和你总结下关于索引开销的最佳实践吧。

  • 第一,无需一开始就建立索引,可以等到业务场景明确后,或者是数据量超过 1 万、查询变慢后,再针对需要查询、排序或分组的字段创建索引。创建索引后可以使用 EXPLAIN 命令,确认查询是否可以使用索引。我会在下一小节展开说明。
  • 第二,尽量索引轻量级的字段,比如能索引 int 字段就不要索引 varchar 字段。索引字段也可以是部分前缀,在创建的时候指定字段索引长度。针对长文本的搜索,可以考虑使用 Elasticsearch 等专门用于文本搜索的索引数据库。
  • 第三,尽量不要在 SQL 语句中 SELECT *,而是 SELECT 必要的字段,甚至可以考虑使用联合索引来包含我们要搜索的字段,既能实现索引加速,又可以避免回表的开销。

索引失效

  1. 索引只能匹配列前缀。原因很简单,索引 B+ 树中行数据按照索引值排序,只能根据前缀进行比较。如果要按照后缀搜索也希望走索引的话,并且永远只是按照后缀搜索的话,可以把数据反过来存,用的时候再倒过来。
  2. 条件涉及函数操作无法走索引。同样的原因,索引保存的是索引列的原始值,而不是经过函数计算后的值。如果需要针对函数调用走数据库索引的话,只能保存一份函数变换后的值,然后重新针对这个计算列做索引。
  3. 联合索引只能匹配左边的列。原因也很简单,在联合索引的情况下,数据是按照索引第一列排序,第一列数据相同时才会按照第二列排序。也就是说,如果我们想使用联合索引中尽可能多的列,查询条件中的各个列必须是联合索引中从最左边开始连续的列。如果我们仅仅按照第二列搜索,肯定无法走索引。

基于成本是否决定走索引

查询数据可以直接在聚簇索引上进行全表扫描,也可以走二级索引扫描后到聚簇索引回表。看到这里,你不禁要问了,MySQL 到底是怎么确定走哪种方案的呢。其实,MySQL 在查询数据之前,会先对可能的方案做执行计划,然后依据成本决定走哪个执行计划。

这里的成本,包括 IO 成本和 CPU 成本:

  • IO 成本,是从磁盘把数据加载到内存的成本。默认情况下,读取数据页的 IO 成本常数是 1(也就是读取 1 个页成本是 1)。
  • CPU 成本,是检测数据是否满足条件和排序等 CPU 操作的成本。默认情况下,检测记录的成本是 0.2。
相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
16天前
|
存储 NoSQL 关系型数据库
为什么MySQL不使用红黑树做索引
本文详细探讨了MySQL索引机制,解释了为何添加索引能提升查询效率。索引如同数据库的“目录”,在数据量庞大时提高查询速度。文中介绍了常见索引数据结构:哈希表、有序数组和搜索树(包括二叉树、平衡二叉树、红黑树、B-树和B+树)。重点分析了B+树在MyISAM和InnoDB引擎中的应用,并讨论了聚簇索引、非聚簇索引、联合索引及最左前缀原则。最后,还介绍了LSM-Tree在高频写入场景下的优势。通过对比多种数据结构,帮助理解不同场景下的索引选择。
52 6
|
13天前
|
SQL 关系型数据库 MySQL
案例剖析:MySQL唯一索引并发插入导致死锁!
案例剖析:MySQL唯一索引并发插入导致死锁!
案例剖析:MySQL唯一索引并发插入导致死锁!
|
7天前
|
存储 关系型数据库 MySQL
Mysql(4)—数据库索引
数据库索引是用于提高数据检索效率的数据结构,类似于书籍中的索引。它允许用户快速找到数据,而无需扫描整个表。MySQL中的索引可以显著提升查询速度,使数据库操作更加高效。索引的发展经历了从无索引、简单索引到B-树、哈希索引、位图索引、全文索引等多个阶段。
38 3
Mysql(4)—数据库索引
|
23小时前
|
存储 关系型数据库 MySQL
如何在MySQL中进行索引的创建和管理?
【10月更文挑战第16天】如何在MySQL中进行索引的创建和管理?
10 1
|
1月前
|
存储 SQL 关系型数据库
【MySQL调优】如何进行MySQL调优?从参数、数据建模、索引、SQL语句等方向,三万字详细解读MySQL的性能优化方案(2024版)
MySQL调优主要分为三个步骤:监控报警、排查慢SQL、MySQL调优。 排查慢SQL:开启慢查询日志 、找出最慢的几条SQL、分析查询计划 。 MySQL调优: 基础优化:缓存优化、硬件优化、参数优化、定期清理垃圾、使用合适的存储引擎、读写分离、分库分表; 表设计优化:数据类型优化、冷热数据分表等。 索引优化:考虑索引失效的11个场景、遵循索引设计原则、连接查询优化、排序优化、深分页查询优化、覆盖索引、索引下推、用普通索引等。 SQL优化。
397 15
【MySQL调优】如何进行MySQL调优?从参数、数据建模、索引、SQL语句等方向,三万字详细解读MySQL的性能优化方案(2024版)
|
16天前
|
监控 关系型数据库 MySQL
MySQL数据表索引命名规范
MySQL数据表索引命名规范
26 1
|
16天前
|
存储 SQL 关系型数据库
mysql中主键索引和联合索引的原理与区别
本文详细介绍了MySQL中的主键索引和联合索引原理及其区别。主键索引按主键值排序,叶节点仅存储数据区,而索引页则存储索引和指向数据域的指针。联合索引由多个字段组成,遵循最左前缀原则,可提高查询效率。文章还探讨了索引扫描原理、索引失效情况及设计原则,并对比了InnoDB与MyISAM存储引擎中聚簇索引和非聚簇索引的特点。对于优化MySQL性能具有参考价值。
|
22天前
|
存储 关系型数据库 MySQL
MySQL中的索引及怎么使用
综上所述,MySQL索引的正确使用是数据库性能调优的关键一环。通过合理设计索引结构,结合业务需求和数据特性,可以有效提升数据库查询响应速度,降低系统资源消耗,从而确保应用的高效运行。
49 1
|
2天前
|
监控 关系型数据库 MySQL
mysql8索引优化
综上所述,深入理解和有效实施这些索引优化策略,是解锁MySQL 8.0数据库高性能查询的关键。
9 0
|
1月前
|
关系型数据库 MySQL 数据库
MySQL删除全局唯一索引unique
这篇文章介绍了如何在MySQL数据库中删除全局唯一的索引(unique index),包括查看索引、删除索引的方法和确认删除后的状态。
64 9