MySQL 索引结构

article/2025/8/17 8:13:04

前言

在上一篇 MySQL 索引类型 中,我们已经了解了索引的基本概念以及分类,那么,索引的结构是什么样的?为什么索引可以这么快?这一篇文章将继续探讨索引的实现原理和数据结构,主要介绍 B+ 树索引和 Hash 索引。

文章目录

  • 前言
  • 数据库存储单位
  • 索引数据结构
    • 二叉树的局限性
    • B 树
    • B+ 树索引
    • HASH 索引
    • FULLTEXT 索引

数据库存储单位

首先我们要知道,由于为了实现持久化,只能将索引存储在硬盘上,通过索引来进行查询的时候就会产生硬盘的 I/O 操作,因此,设计索引时需要尽可能的减少查找次数,从而减少 I/O 耗时。

此外还需要知道一个很重要的原理:数据库管理存储空间的基本单位是页(Page),一个页中存储多条行记录(Row)。

计算机系统对磁盘 I/O 会做预读优化,当一次I/O时,除了当前磁盘地址的数据以外,还会把相邻的数据也读取到内存缓冲池中,每一次 I/O 读取的数据成为一页,InnoDB 默认的页大小是 16KB。
在这里插入图片描述
连续的 64 个页组成一个区(Extent),一个或多个区组成一个段(Segment),一个或多个段组成表空间(Tablespace)。InnoDB 有两种表空间类型,共享表空间表示多张表共享一个表空间,独立表空间表示每张表的数据和索引全部存在独立的表空间中。

数据页结构如下(图源:极客时间《MySQL 必知必会》):
在这里插入图片描述
数据页的 7 个结构内容可以大致分为以下三类:

  • 文件通用部分,用于校验页传输完整
    • 文件头(File Header): 表述页信息,文件头中使用 FIL_PAGE_PREV 和 FIL_PAGE_NEXT 构成一个双向链表,分别指向前后的数据页。
    • 页头(File Header):记录页的状态信息
    • 文件尾(File Trailer): 校验页是否完整
  • 记录部分,用于存储数据记录
    • 最大最小记录(Infimum/Supremum):虚拟的行记录,表示数据页的最大记录和最小记录。
    • 用户记录(User Record)和空闲空间(Free Space): 用于存储数据行记录内容
  • 索引部分,用于提高记录的检索效率
    • 页目录(Page Directory):存储用户记录的相对位置

详情可参考淘宝的数据库内核月报

索引数据结构

很自然的,我们会想到查找算法中涉及到的一些常用数据结构,比如二叉查找树,二叉平衡树等等,实际上,Innodb 的索引是用 B+ 树 来实现的,下面我们来看看为何会选择这种索引结构。

二叉树的局限性

先来简单回顾一下二叉搜索树(Binary Search Tree)的定义,二叉搜索树中,如果要查找的 key 大于根节点,则在右子树中搜索,如果 key 小于根节点,则在左子树中搜索,直到找到 key 为止,时间复杂度为 O(logn)。比如数列 [4,2,6,1,3,5,7],会生成如下二叉搜索树:
在这里插入图片描述
但是在某些特殊情况下,二叉树的深度会非常大,比如 [1,2,3,4,5,6,7],则会生成如下的树:
在这里插入图片描述
在下面这种情况中,最坏的情况下需要查 7 次才能够查到想要的结果,查询时间变成了 O(n)。

为了优化这种情况,就有了平衡二叉搜索树(AVL 树),AVL 树是指左右子树的高度相差不超过 1 的树,搜索时间复杂度为 O(logn),这已经是比较理想的搜索树了,但是在动辄几千万行记录的数据库中,树的深度还是会很高,依然不是最理想的结构。

B 树

那么,如果从二叉树扩展到 N 叉树呢,很容易想象到,N 叉树可以大大的减少树的深度,实际上,4 层树结构就已经可以支撑几十 T 的数据了。

B 树(Balance Tree)就是这样的一种 N 叉树, B 树也称为 B- 树,满足如下定义:
设 k 为 B 树的度 (degree, 表示每个节点最多能有多少个子节点),

  1. 每个磁盘块中最多包含 k - 1 个关键字 和 k 个子节点的指针
  2. 叶子节点中,只有关键字,没有子节点指针
  3. 每个结点中的关键字都按照从小到大的顺序排列,每个关键字的左子树中的所有关键字都小于它,而右子树中的所有关键字都大于它。
  4. 所有叶子节点位于同一层。

上面已经提到,每一次 I/O 会预读一个磁盘块的数据,大小为一页,用一个磁盘块的内容表示一次 I/O,B 树的结构如下图 (图源:极客时间 SQL 必知必会):
在这里插入图片描述
B 树也是有序的,由于子节点指针一定比关键字多 1,所以正好可以用关键字划分子节点的区段,如图中的例子,每个节点有 2 个关键字,3 个子节点,如磁盘块 2 ,第一个字节点的关键字 3,5 小于自身的第一个子节点 8,第二个子节点的 9,10 在 8 和 12 之间,第三个子节点的值 13,15 大于自身的第二个子节点 12。

假设我们现在要查找 9,步骤如下:

  1. 与根节点磁盘块 1 (17,35) 比较,小于 17,继续在指针 P1 中查找,对应磁盘块 2
  2. 与磁盘块 2 (8,12) 比较,位于两者之间,继续在指针 P2 查找,对应磁盘块 6
  3. 与磁盘块 6 (9, 10) 比较,找到 9

可以看到,虽然做了很多次比较的操作,但是由于进行了预读,所以在磁盘块内部的比较是在内存中进行的,不耗费磁盘 I/O,上述操作只需要进行 3 次 I/O 即可完成,已经是比较理想的结构了。

B+ 树索引

B+ 树在 B 树的基础上进行了进一步的改进,B+ 树和 B 树的区别有以下几点:

  1. B+ 树的构建方式是,对于父节点中的关键字,左子树的所有关键字小于它,右子树的所有关键字都大于等于它
  2. 非叶子节点仅用于索引,不会存储数据记录
  3. 父节点的关键字也会出现在子节点中,并且都是子节点中的最大值(或者最小值)
  4. 所有关键字都会出现在叶子节点中,叶子节点构成一个有序链表,按从小到大排序。

示例如下,本例中,父节点的关键字都是子节点中的最小值 (图源:极客时间 SQL 必知必会):在这里插入图片描述
假设要查找关键字 16,查找步骤如下:

  1. 与根节点磁盘 1 (1,18,35) 比较,16 在 1 和 18 之间,得到指针 P1,指向磁盘 2
  2. 找到磁盘 2 (1,8,14),16 大于 14,得到指针P3,指向磁盘 7
  3. 找到磁盘 7 (14,16,17),找到16

B+ 树优点:

  1. 内部节点不存储数据,因此每个内部节点可以存储的记录数量远大于 B树,树的高度更低,I/O 更少,每次 I/O 读取的数据页里内容更多
  2. 可以支持范围查询,直接在叶子节点组成的有序链表遍历即可
  3. 所有数据都存储在叶子节点,因此查询效率更稳定

HASH 索引

MySQL 的 memory 存储引擎默认的索引结构是 Hash 索引,Hash 是一种函数, 称为散列函数,通过特定算法(如 MD5, SHA1,SHA2 等)将任意长度的输入转换为固定长度的输出,输入和输出一一对应,本文不会对 hash 函数做深入的介绍,详情请参考 百度百科。

Hash 查找的效率为 O(1),效率非常高,python 的 dict,golang 中的 map,java 中的 hash map 都是基于 hash 实现的,在 Redis 这样的 Key-Value 数据库也是由 Hash 实现。

对于精确查找而言,Hash 索引的效率会比 B+ 树索引更高,但是 Hash 索引有一些局限性,因此不是最主流的索引结构。

  1. 因为 Hash 索引指向的数据是无序的,所以Hash 索引不能范围查询,也不支持 ORDER BY 排序。
  2. 由于 Hash 是精确匹配,因此也不能进行模糊查询。
  3. Hash 索引不支持联合索引的最左匹配原则,联合索引只有在完全匹配时生效。因为 Hash 索引计算 Hash 值的时候是将索引合并后再一起计算 Hash 值,而不会计算每个索引的单独 Hash 值。
  4. 如果被索引字段的重复值很多,那就会造成大量的 Hash 冲突,这时候查询就会变得非常耗时。

基于上述原因考虑,Mysql InnoDB 引擎不支持 Hash 索引,但是在内存结构中有一个自适应 Hash 索引的功能,当某个索引值使用非常频繁的时候,会在 B+ 树索引的基础上自动创建一个 Hash 索引,来提高查询性能。

自适应 Hash 索引可以理解为一种 “索引的索引”,采用 Hash 索引储存 B+ 树索引中的页面地址,迅速定位到对应的叶子节点。可以通过 innodb_adaptive_hash_index 变量来查看。

FULLTEXT 索引

上一篇 MySQL 索引类型 已经介绍了全文索引的使用方法,由于全文索引使用场景较少,篇幅限制,本文不对全文索引的底层结构做深入介绍,详情请参考官网。


http://chatgpt.dhexx.cn/article/KA3TVJsN.shtml

相关文章

MySQL中索引的使用方法

1. 为什么要加索引? ​一般的应用系统,读写比例在10:1左右,而且插入和一般的更新操作很少出现性能问题,遇到最多的,也是最容易出问题的,还是一些复杂的查询操作,所以查询语句的优化显然是重中之…

MySQL 索引概览

前言 在 SQL 优化中,索引是至关重要的一环,能给查询效率带来质的飞跃,但是索引并不是万能的,不合理的索引设计甚至会拖慢查询效率。本文将详细介绍索引的概览和分类,并讨论使用索引时应该权衡的要素,关于索…

MYSQL的索引和存储引擎

文章目录 MYSQL的索引和存储引擎介绍索引的分类单列索引-普通索引单列索引-唯一索引单列索引-主键索引组合索引全文索引空间索引 索引内部原理剖析索引内部原理-Hash算法索引内部原理-二叉树和二叉平衡树索引内部原理-BTREE树MyISAM存储引擎InnoDB存储引擎 索引的特点索引的创建…

mysql 索引使用与优化

前言 索引对有一定开发经验的同学来说并不陌生,合理使用索引,能大大提升sql查询的性能,可以这么讲,随着业务数据量的不断增长,优化系统的响应速度,很大程度上可以说就是集中在索引的优化上; mysql索引原理 在正式了解与学习mysql索引之前,先对mysql的索引原理再次回…

MySql之索引

1.索引概述 MySql官方对索引的定义为:索引是帮助MySql高效获取数据的数据结构。在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用数据,这样就可以在这些数据结构上实现高级查找算法&#xf…

Mysql 索引

图片来源网络,侵删。图片来源于掘金小册 索引 Mysql 的索引类型有很多种,Hash索引,B树索引,B树索引和全文索引。Mysql有多种存储引擎,每个存储引擎对索引的支持可能会不同。 What Mysql 索引是能改善数据库表随机访…

一文搞懂 MySQL 索引

一文搞懂 MySQL 索引 1、MySQL 索引 简介 1.1、MySQL 索引 是什么? 索引是一个单独的、存储在 磁盘 上的 数据库结构 ,包含着对数据表里 所有记录的 引用指针。 1.2、 MySQL 索引 的存储类型有哪些? MySQL中索引的存储类型有两种&#xff0c…

一文搞懂MySQL索引所有知识点(建议收藏)

Mysql索引 索引介绍 索引是什么 官方介绍索引是帮助MySQL高效获取数据的数据结构。更通俗的说,数据库索引好比是一本书前面的目录,能加快数据库的查询速度。 一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往是存储…

python中的%用法

python中%: 1. 求模运算,相当于mod,也就是计算除法的余数,比如5%2就得到1。 2. %还用在python的格式化输出,比如: 说明如下: %[(name)][flags][width].[precision]typecode (name) 为命名 fl…

python中的消息弹窗

在写python代码中,经常要弹窗提示一下消息情况,因为有时候我同时用了多个ui框架,比如tkinter,pyqt等,经常找不到合适的弹窗模块。因此梳理了一下几种弹窗方案。 一、采用windows自带的api(需要导入win32api) 特别强调采用这种方案,这种方案的优势就是弹窗模态,并不需…

Python 中的\r 字符

今天遇到了\r,然后就比较懵了,这里简单记录一下\r字符在Python中的应用。 \r:将光标回退到开始位置 先来看一个示例代码: import timetext "Hello\rWorld!" for i in text:time.sleep(0.5)print(i, end"")…

python中flag的用法_python中flag什么意思

python中flag一般就是标记、标识的意思 比如&#xff1a;&#xff08;推荐学习&#xff1a;Python视频教程&#xff09;#!/usr/bin/python # -*- coding: UTF-8 -*- x 7 i 1 flag 0 while i < 100: if (x%2 1) and (x%3 2) and (x%5 4) and (x%65): flag 1 e…

python中result的用法_python中result的用法

Python中%(number,result)是什么意思 浮点型(Float) Python的浮点数就是数学中的小数&#xff0c;类似C语言中的double。 在运算中&#xff0c;整数与浮点数运算的结果是浮点数. 浮点数也就是小数&#xff0c;之所以称为浮点数&#xff0c;是因为按照科学记数法表示时&#xf…

解决python中文乱码问题

python输出中文乱码的问题相信大家都遇到过 那么应该如何解决呢&#xff1f; 一、修改系统变量 依次打开 设置->系统->关于->高级系统设置->环境变量->新建系统变量&#xff0c;新变量的变量名是&#xff1a;PYTHONIOENCODING&#xff0c;变量值是&#xff1…

python中value的含义_python中value的意思

python语句s = str(value)是什么意思呢 把value转成字符串,然后赋值给变量s 比如说,s=str(100)以后。 Python 比如有一串数据的话里面 value[:-1]是什么value[:-1] : value 应该是一个列表/元组, value[:-1]表示其最后一个元素 python 如何将字典中的value值CSS布局HTML小编…

python中注释

python中注释 在python中的注释一般分为单行注释、多行注释以及文档注释。 注释描述 在实际开发过程中&#xff0c;有效的代码注释不仅可以提升个人的工作效率&#xff0c;快速了解自己的程序情况&#xff0c;在团队协作开发过程中可以更加方便地让同事学习和调用你的代码。单…

python中的各种符号

运算符描述实例算术运算符%取模 - 返回除法的余数b % a 输出结果 0**幂 - 返回x的y次幂a**b 为10的20次方&#xff0c; 输出结果 100000000000000000000//取整除 - 返回商的整数部分&#xff08;向下取整&#xff09; >>> 9//2 4 >>> -9//2 -5 赋值运算…

python中的''''''字符串真的那么简单么?

文章目录 多行字符串&#xff0c;且保留代码格式&#xff01;文档&#xff01;&#xff01;&#xff01;注释功能 开门见山地说&#xff0c;如果你是一个接触Python一段时间的读者。那么你一定知道’和""可以灵活使用&#xff0c;例如以下的场景&#xff1a; s &quo…

Python基础篇

python是众多编程语言里面较为高级但也较慢的语言。所以&#xff0c;灵活地使用python内置的类和方法既可以减少代码量&#xff0c;也可以提高代码的运行速度。本文我们主要归纳python的一些基础知识&#xff0c;包括基本的类&#xff0c;内置方法&#xff0c;以及文件操作的一…

python中的函数(全)

函数的定义 概述&#xff1a;将一段经常使用的函数封装起来&#xff0c;减少重复代码&#xff0c;一个较大的程序&#xff0c;一般分为若干个程序块&#xff0c;每个模块实现特定的功能 于python中&#xff0c;定义函数时要用到def 语法结构&#xff1a; def 函数名称&…