MySQL深度剖析-InnoDB索引与B+树

1. 什么是B+树?

B + 树是一种自平衡的多叉树,它是 B 树的一种变体。与 B 树不同,B + 树的所有数据都存储在叶子节点,非叶子节点仅存储索引,且叶子节点之间通过双向链表相连。这种结构使得 B + 树在范围查询和排序操作上具有更高的效率。

B + 树的特点包括:

  • 平衡:随着数据的插入和删除,B + 树会自动调整结构,保持平衡,确保查询时间稳定。
  • 有序:叶子节点中的数据按顺序排列,便于范围查询和排序。
  • 高扇出:每个节点可以有多个子节点,减少树的高度,提高查询效率。
  • 缓存友好:适合现代计算机的缓存机制,减少磁盘 I/O。

B+树核心特性解析:

  • 多路平衡树结构:每个节点可存储多个键值(典型为1200+)
  • 层级控制专家:3-4层即可存储千万级数据
  • 叶子链表连接:所有数据节点形成双向链表
  • 非叶仅存索引:中间节点仅保存导航键值
  • 绝对平衡特性:所有叶子节点处于同一深度

2. 为什么使用B+树作为索引的数据结构?

B+树与常见数据结构的对比:

结构类型查询复杂度插入复杂度范围查询磁盘友好度
哈希表O(1)O(1)不支持
二叉搜索树O(logN)O(logN)支持
AVL树O(logN)O(logN)支持
B树O(logN)O(logN)支持
B+树O(logN)O(logN)极优

MySQL 选择 B + 树作为索引的数据结构,主要原因如下:

  • 范围查询高效:由于叶子节点有序且相连,B + 树可以快速定位某个区间内的数据,适合范围查询。
  • 减少磁盘 I/O:B + 树的高扇出特性使得树的高度降低,减少了磁盘 I/O 次数,提高查询性能。
  • 数据存储集中:所有数据都存储在叶子节点,使得数据存储更集中,便于管理和维护。
  • 插入和删除高效:B + 树的自平衡特性保证了插入和删除操作的效率,不会因为数据的变化而导致性能大幅下降。

3. 贯穿本文索引的数据例子

为了更好的理解,之后的聚簇索引、二级索引、联合索引的图例等都用到的是下面的例子:

  1. 建表:
CREATE TABLE `anarkh_slave`.`index_demo` (`c1` INT NOT NULL,`c2` INT NULL,`c3` CHAR(1) NULL,PRIMARY KEY (`c1`)) ROW_FORMAT = COMPACT;
  1. 插入数据
INSERT INTO `anarkh_slave`.`index_demo` (`c1`, `c2`, `c3`) VALUES ('1', '4', 'u');
INSERT INTO `anarkh_slave`.`index_demo` (`c1`, `c2`, `c3`) VALUES ('3', '9', 'd');
INSERT INTO `anarkh_slave`.`index_demo` (`c1`, `c2`, `c3`) VALUES ('4', '4', 'a');
INSERT INTO `anarkh_slave`.`index_demo` (`c1`, `c2`, `c3`) VALUES ('5', '3', 'y');
INSERT INTO `anarkh_slave`.`index_demo` (`c1`, `c2`, `c3`) VALUES ('8', '7', 'a');
INSERT INTO `anarkh_slave`.`index_demo` (`c1`, `c2`, `c3`) VALUES ('10', '4', 'o');
INSERT INTO `anarkh_slave`.`index_demo` (`c1`, `c2`, `c3`) VALUES ('12', '7', 'd');
INSERT INTO `anarkh_slave`.`index_demo` (`c1`, `c2`, `c3`) VALUES ('20', '2', 'e');
INSERT INTO `anarkh_slave`.`index_demo` (`c1`, `c2`, `c3`) VALUES ('100', '9', 'x');
INSERT INTO `anarkh_slave`.`index_demo` (`c1`, `c2`, `c3`) VALUES ('209', '5', 'b');
INSERT INTO `anarkh_slave`.`index_demo` (`c1`, `c2`, `c3`) VALUES ('220', '6', 'i');
INSERT INTO `anarkh_slave`.`index_demo` (`c1`, `c2`, `c3`) VALUES ('300', '8', 'a');
INSERT INTO `anarkh_slave`.`index_demo` (`c1`, `c2`, `c3`) VALUES ('320', '5', 'm');

准备好了数据,我们还需要回顾一些知识:

  1. 首先是行格式,我们新建的index_demo表是使用COMPACT行格式来存储记录的,为了方便理解,将index_demo表的行格式简化为下图所示:

  1. 接下来还需要回顾一下记录在页中是如何放置的。下图为记录放到页里面的示意图:

准备好了这些,就可以继续探讨索引了。

4. 聚簇索引

4.1 概念

聚簇索引并不是一种单独的索引类型,而是一种数据存储方式。在 InnoDB 存储引擎中,聚簇索引的数据行和相邻的键值紧密存储在一起,通常以主键作为聚簇索引。

通常来说,把具有以下两个特点的B+树称为聚簇索引:

  1. 使用记录主键值的大小进行记录和页的排序,这包括3方面的含义:
    1. 页(包括叶子节点和内节点)内的记录按照主键的大小顺序排成一个单向链表,页内的记录被划分成若干组,每个组中主键值最大的记录在页内的偏移量会被当作槽依次放在页目录中(当然Supremum记录比任何用户记录都大),我们可以在页目录中通过二分法快速定位到主键列等于某个值的记录。
    2. 各个存放用户记录的页也是根据页中用户记录的主键大小顺序排成一个双向链表。
    3. 存放目录项记录的页分为不同的层级,在同一层级中的页也是根据页中目录项记录的主键大小顺序排成一个双向链表。
  2. B+树的叶子节点存储的是完整的用户记录。所谓完整的用户记录,就是指这个记录中存储了所有列的值(包括隐藏列)。

也就是说页与页之间是双向链表,页内记录是通过单向链表进行连接。

歪个题,说一下单向链表和双向链表的优缺点:

双向链表

  • 优点
    • 双向遍历:从双向链表中的任意一个结点开始,都可以很方便地访问前驱结点和后继结点,可进可退,在需要频繁双向查找的场景中优势明显,比如实现浏览器的历史记录功能,用户可以方便地进行前进和后退操作125。
    • 查找效率高:对于某些需要查找前一个节点的操作,双向链表无需像单向链表那样从头开始遍历,能直接通过指针找到前一个节点,提高了查找效率。在已知当前节点的情况下,查找其前驱节点的时间复杂度为 O (1)356。
    • 删除操作灵活:删除节点时,可以快速定位前后节点,进行相应的指针调整。若已知要删除的节点,直接修改其前后节点的指针即可完成删除,无需像单向链表那样需要保存前一个节点的指针57。
    • 数据安全性高:指针机制使得数据更难丢失,因为即使丢失了尾节点,仍然可以通过尾节点的后指针访问到链表5。
  • 缺点
    • 空间开销大:每个节点除了存储数据和一个指向下一节点的指针外,还需要额外存储一个指向前一节点的指针,因此占用的存储空间比单向链表多124。
    • 操作复杂度高:插入和删除节点时,需要同时修改前一个节点和后一个节点的指针,操作相对复杂,实现代码量也相对较多56。
    • 内存对齐问题:由于节点中有两个指针,在内存对齐方面可能会面临一些挑战,需要更多地考虑内存管理和分配问题5。

单向链表

  • 优点
    • 结构简单:每个节点只包含数据和指向下一个节点的指针,实现和操作较为容易,理解和编写相关代码相对轻松567。
    • 节省空间:不需要额外的指针来存储前一个节点的地址,相比双向链表,每个节点占用的存储空间更少,在存储大量数据时,能节省一定的内存空间124。
    • 插入删除简单:在进行插入和删除操作时,只需修改相邻节点的指针,不需要移动其他大量元素。例如在头部插入一个新节点,只需让新节点的指针指向原来的头节点,然后更新头指针即可,时间复杂度为 O (1)56。
  • 缺点
    • 单向遍历:只能从头到尾单向遍历,无法直接访问前一个节点,若需要反向遍历,需要从头开始重新遍历整个链表,效率较低136。
    • 查找效率低:查找节点时,需要从头部开始逐个节点进行比较,直到找到目标节点,平均时间复杂度为 O (n),在链表长度较大时,查找速度较慢3。
    • 删除操作限制:删除节点时,如果不知道要删除节点的前一个节点,就需要从头遍历链表来找到前一个节点,才能进行删除操作,增加了操作的复杂性和时间成本45。

4.2 本质特征

  1. 主键索引即数据:<font style="color:rgb(64, 64, 64);">叶节点=数据页</font>
  2. 物理有序存储:相邻主键的数据行物理相邻
  3. 自动创建规则:没有主键时隐式创建6字节ROWID
  4. 索引覆盖扫描:可以直接使用叶节点中的主键值

4.3 图例解析

从根节点开始查找数据,如果用户记录的主键值在[1,320)之间,则到页30中查找更详细的目录项记录,如果主键值不小于320,就到32页中查找。下面节点依次类推。

5. 二级索引

5.1 概念

把以非主键列的大小为排序规则而建立的B+树,且需要执行回表操作才可以定位到完整用户记录的B+树称为二级索引,也叫辅助索引。

5.2 结构特征

  1. 叶节点存储了索引键值和主键值
  2. 非页节点存储了索引键值和指针(页号)
  3. 需要回表操作(通过携带主键信息到聚簇索引中重新定位完整的用户记录的过程称为回表操作)

5.3 图例解析

  • B+树的叶子节点存储的并不是完整的用户记录,而只是c2列+主键这两个列的值。
  • 目录项记录中不再是主键+页号的搭配,而变成了c2列+页号的搭配。

6. 联合索引

6.1 概念

联合索引是指在多个列上创建的索引。

6.2 核心特征

  • 多列组合索引:最多支持16列(建议不超过5列)
  • 有序排列规则:先按第一列排序,第二列在第一列相同的情况下排序
  • 索引复用特性:可以支持多种查询条件组合

注意事项

最左前缀原则:查询条件必须按照索引列的顺序依次出现,否则索引可能不会被使用。例如,如果索引是name和age,那么查询WHERE name = 'Alice’会使用索引,但WHERE age = 25则不会。

覆盖索引:如果查询的所有列都在索引中,那么可以直接从索引中获取数据,而不需要回表。例如,SELECT name, age FROM users WHERE name = ‘Alice’。

6.3 联合索引设计四象限

  1. 高频查询组:将WHERE中最常出现的列放在左边
  2. 基数梯度组:高区分度列优先(城市 > 性别)
  3. 排序需求组:包含ORDER BY/GROUP BY的列
  4. 覆盖索引组:包含SELECT需要的所有列

6.4 图例解析

7. 数据页中的槽与记录中的next_record

  • InnoDB页结构核心组件
    • 槽(Slot):页目录中的二分查找锚点
    • next_record:行记录间的单向链表指针
    • 最大最小记录:页的边界标记
  • 记录分组与槽的形成:一个数据页里的确包含多条记录,这些记录会按照规则进行分组。每组的最后一条记录通常是该组内主键值(或索引键值)最大的记录,将每组最后一条记录的地址偏移量按顺序存放在页目录中,这些偏移量就是槽。槽起到了对页内记录进行索引的作用,能帮助快速定位记录所在的组。
  • **next_record**** 属性**:next_record 是每条记录都具备的属性,它以相对偏移量的形式,指向数据页内的下一条记录,从而将数据页中的记录连接成一个单向链表。
  • 查找流程:在数据页内查找特定记录时,首先会使用二分查找法在页目录中查找槽,以此确定目标记录所在的组。然后在该组内,通过 next_record 属性遍历记录链表,直至找到目标记录。
  • 记录排序规则:记录一般按照主键值(如果是聚集索引)或索引键值(如果是非聚集索引)从小到大的顺序进行排序。在分组时,也是基于这个排序结果进行划分的。
  • 槽的二分查找:在页目录中使用二分查找槽时,比较的是槽所指向记录的键值。通过不断缩小查找范围,快速定位到可能包含目标记录的组。
  • 虚拟记录与链表遍历:数据页中存在两条虚拟记录,即最小记录和最大记录。最小记录位于数据页的起始位置,最大记录位于数据页的末尾位置,它们也参与到记录链表的组织中。在组内遍历记录时,从该组的第一条记录开始,依据 next_record 属性依次访问下一条记录,直到遍历完该组的所有记录或者找到目标记录。
  • 示例辅助理解

假设一个数据页存储了学生记录,按照学生的 ID 从小到大排序并分组。页目录中有多个槽,分别指向每组的最后一条记录。当要查找 ID 为 10 的学生记录时,首先在页目录中通过二分查找槽,确定 ID 为 10 的记录可能所在的组。然后在该组内,从第一条记录开始,利用 next_record 属性依次遍历记录,直到找到 ID 为 10 的记录。

8. B+树索引黄金法则

  1. 页分裂代价:插入无序数据可能导致50%页分裂
  2. 填充因子控制:默认预留1/16空间用于更新
  3. 索引选择性:基数/总行数 > 0.2 推荐建索引
  4. 覆盖索引优化:避免回表的终极方案
  5. 最左前缀原则:联合索引的左优先匹配
  6. 索引下推技术:5.6+版本的条件过滤优化
  7. MRR优化:随机IO转顺序IO的缓冲机制
  8. 索引合并:OR条件的优化策略
  9. 隐式转换陷阱:VARCHAR字段用数字查询
  10. 函数计算禁区:WHERE YEAR(create_time)=2023
  11. 前缀索引权衡:节省空间但影响排序
  12. 唯一索引代价:检查唯一性带来的性能损耗
  13. 列顺序法则:等值查询列在前,范围查询列在后
  14. 长度控制法则:单列索引长度总和不超过3072字节
  15. 索引合并预警:出现index_merge可能暗示需要联合索引
  16. 前缀索引陷阱:联合索引中的前缀索引会阻断后续列使用

9. 索引优化实战策略

9.1 创建策略

  • 优先选择WHERE/JOIN/ORDER字段
  • 联合索引列顺序:区分度高的列在前
  • 控制单表索引数量(建议不超过5个)

9.2 注意事项

  • 索引字段选择:选择选择性高(即字段值的重复度低)的字段作为索引,能提高索引的效率。
  • 前缀索引:对于较长的字段,可以使用前缀索引,减少索引占用的空间,但要注意前缀长度的选择,避免降低索引的选择性。
  • 索引维护:定期维护索引,如重建索引,以保持索引的性能。

9.3 使用禁忌

-- 反例:模糊查询失效
SELECT * FROM products WHERE name LIKE '%手机%';-- 正例:使用覆盖索引
SELECT id FROM products WHERE name LIKE '小米%';

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

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

相关文章

win11系统 Docker Desktop提示Docker Engine stopped解决全过程记录

DockerDesktop安装指南以及Windows下WSL2和 Hyper-V相关问题追查 【已解决】win10系统 Docker 提示Docker Engine stopped解决全过程记录 本篇文章主要记录Docker Desktop安装和使用时出现的问题及解决方法&#xff0c;以及后续使用夜神模拟器&#xff0c;关闭了Hyper-V时&am…

如何使用UniApp实现页面跳转和数据传递?

在 UniApp 中&#xff0c;页面跳转和数据传递是基本的功能&#xff0c;允许用户在应用中浏览不同的页面并传递必要的信息。以下是如何实现页面跳转和数据传递的详细步骤和示例。 一、页面跳转 UniApp 提供了几种方式来进行页面跳转&#xff0c;主要包括&#xff1a; uni.nav…

VSCode配合cline实现自动编程

VS Code是微软开发的代码编辑器&#xff0c;可以配合安装不同的插件&#xff0c;支持对不同语言、项目类型的开发。 &#xff08;1&#xff09;安装cline插件&#xff1a;在vscode扩展商店搜索cline&#xff0c;并安装&#xff1b; &#xff08;2&#xff09;选择不同的大模型…

Docker 镜像标签使用

写在前面 当使用命令 docker pull mysql 拉取镜像时&#xff0c;其实等价于如下命令 docker pull mysql:latest latest 是默认的标签&#xff0c;字面上理解为最新版本的镜像&#xff0c;实质上 latest 只是镜像的标签名称&#xff0c;跟具体某个版本号地位一样&#xff0c;…

CHARMM-GUI EnzyDocker: 一个基于网络的用于酶中多个反应状态的蛋白质 - 配体对接的计算平台

❝ "CHARMM-GUI EnzyDocker for Protein−Ligand Docking of Multiple Reactive States along a Reaction Coordinate in Enzymes"介绍了 CHARMM-GUI EnzyDocker&#xff0c;这是一个基于网络的计算平台&#xff0c;旨在简化和加速 EnzyDock 对接模拟的设置过程&…

mysql 使用 CONCAT、GROUP_CONCAT 嵌套查询出 json 格式数据

tb_factory表由 factory_code 和 factory_name 字段&#xff0c;查询出如下所示效果&#xff1a; {"factory_0001": "工厂1","factory_0002": "工厂2",... } select sql&#xff1a; SELECT CONCAT( "{",GROUP_CONCAT( C…

Docker__持续更新......

Docker 1. 基本知识1.1 为什么有Docker?1.2 Docker架构与容器化 画图解释 画图解释2. 项目实战 1. 基本知识 1.1 为什么有Docker? 用一行命令跨平台安装项目&#xff0c;在不同平台上运行项目。把项目打包分享运行应用。 1.2 Docker架构与容器化 准备机器&#xff0c;在机…

解决 `pip is configured with locations that require TLS/SSL` 错误

问题描述 在使用 pip 安装 Python 包时&#xff0c;可能会遇到以下错误&#xff1a; WARNING: pip is configured with locations that require TLS/SSL, however the ssl module in Python is not available.这意味着 Python 的 ssl 模块未正确安装或配置&#xff0c;导致 p…

网络安全不分家 网络安全不涉及什么

何为网络安全 信息安全是指系统的硬件、软件及其信息受到保护&#xff0c;并持续正常运行和服务。信息安全的实质是保护信息系统和信息资源免受各种威胁、干扰和破坏&#xff0c;即保证信息的安全性。 网络安全是指利用网络技术、管理和控制等措施&#xff0c;保证网络系统和…

动手学Agent——Day2

文章目录 一、用 Llama-index 创建 Agent1. 测试模型2. 自定义一个接口类3. 使用 ReActAgent & FunctionTool 构建 Agent 二、数据库对话 Agent1. SQLite 数据库1.1 创建数据库 & 连接1.2 创建、插入、查询、更新、删除数据1.3 关闭连接建立数据库 2. ollama3. 配置对话…

android 的抓包工具

charles 抓包工具 官网地址 nullCharles Web Debugging Proxy - Official Sitehttps://www.charlesproxy.com/使用手册一定记得看官网 SSL Certificates • Charles Web Debugging Proxy http请求&#xff1a; 1.启动代理&#xff1a; 2.设置设备端口 3.手机连接当前代理 …

DeepSeek从入门到精通(清华大学)

​ DeepSeek是一款融合自然语言处理与深度学习技术的全能型AI助手&#xff0c;具备知识问答、数据分析、编程辅助、创意生成等多项核心能力。作为多模态智能系统&#xff0c;它不仅支持文本交互&#xff0c;还可处理文件、图像、代码等多种格式输入&#xff0c;其知识库更新至2…

数据结构:栈(Stack)及其实现

栈&#xff08;Stack&#xff09;是计算机科学中常用的一种数据结构&#xff0c;它遵循先进后出&#xff08;Last In, First Out&#xff0c;LIFO&#xff09;的原则。也就是说&#xff0c;栈中的元素只能从栈顶进行访问&#xff0c;最后放入栈中的元素最先被取出。栈在很多应用…

pdf-extract-kit paddle paddleocr pdf2markdown.py(效果不佳)

GitHub - opendatalab/PDF-Extract-Kit: A Comprehensive Toolkit for High-Quality PDF Content Extraction https://github.com/opendatalab/PDF-Extract-Kit pdf2markdown.py 运行遇到的问题&#xff1a; 错误&#xff1a; -------------------------------------- C Tra…

深度学习之图像回归(一)

前言 图像回归任务主要是理解一个最简单的深度学习相关项目的结构&#xff0c;整体的思路&#xff0c;数据集的处理&#xff0c;模型的训练过程和优化处理。 因为深度学习的项目思路是差不多的&#xff0c;主要的区别是对于数据集的处理阶段&#xff0c;之后模型训练有一些小…

DFS算法篇:理解递归,熟悉递归,成为递归

1.DFS原理 那么dfs就是大家熟知的一个深度优先搜索&#xff0c;那么听起来很高大尚的一个名字&#xff0c;但是实际上dfs的本质就是一个递归&#xff0c;而且是一个带路径的递归&#xff0c;那么递归大家一定很熟悉了&#xff0c;大学c语言课程里面就介绍过递归&#xff0c;我…

HepG2细胞复苏实验以及六孔板种植细胞实验

一、细胞复苏实验&#xff1a; 首先先用紫外照射复苏细胞的新培养皿&#xff0c;然后预热要用到的1640培养基&#xff08;控制在30mins以内&#xff0c;否则会发生蛋白质结构转变等&#xff09;&#xff0c;等待培养基预热完毕后。取出冻存的HepG2细胞&#xff0c;手拿头部在水…

springboot021-基于协同过滤算法的个性化音乐推荐系统

&#x1f495;&#x1f495;作者&#xff1a; 小九学姐 &#x1f495;&#x1f495;个人简介&#xff1a;十年Java&#xff0c;Python美女程序员一枚&#xff0c;精通计算机专业前后端各类框架。 &#x1f495;&#x1f495;各类成品Java毕设 。javaweb&#xff0c;ssm&#xf…

Vue 3最新组件解析与实践指南:提升开发效率的利器

目录 引言 一、Vue 3核心组件特性解析 1. Composition API与组件逻辑复用 2. 内置组件与生命周期优化 3. 新一代UI组件库推荐 二、高级组件开发技巧 1. 插件化架构设计 2. 跨层级组件通信 三、性能优化实战 1. 惰性计算与缓存策略 2. 虚拟滚动与列表优化 3. Tree S…

github用户名密码登陆失效了

问题&#xff1a; git push突然推代码需要登陆&#xff0c;但是用户名和密码正确输入后&#xff0c;却提示403 git push# Username for https://github.com: **** #Password for https://gyp-programmergithub.com: #remote: Permission to gyp-programmer/my-app.git denie…