当前位置: 首页 > article >正文

InnoDB索引结构

文章目录

  • 索引数据结构
    • 聚集索引
    • 非聚集索引
  • 索引维护
    • 顺序插入和随机插入
    • 为什么预留空间
  • 索引构建过程
  • 页的合并拆分

索引数据结构

https://dev.mysql.com/doc/refman/8.4/en/innodb-physical-structure.html
除了空间索引(spatial index)使用R-tree之外,InnoDB索引数据结构都是B+树。每个节点都是一个Page,Page是InnoDB存储记录的最小单位。非叶子节点存储指向子节点的 key 和指针的值。叶子节点存储记录和指向相邻叶节点的指针。索引记录存储在其B+树的叶子节点Page中。索引页的默认大小为16KB。页的大小由MySQL实例初始化时的设置项innodb_page_size设置决定。

聚集索引

每个InnoDB表都有一个特殊的索引,称为聚集索引。聚集索引的功能不单单只是索引,更重要的是,它用于存储引擎为InnoDB的表的所有数据行。

一个表最多只能有一个聚集索引,下面是聚集索引的选取规则:

  1. 当有主键时,则将主键用作聚集索引
  2. 当没有主键时,使用第一个NOT NULL UNIQUE索引作为聚集索引。
  3. 如果以上都没有,InnoDB会在包含行ID值的合成列上生成一个名为GEN_CLUST_INDEX的隐藏聚集索引

在这里插入图片描述

聚集索引按 key 也就是主键值顺序存储记录,父节点的 key 会在子节点复制一份,并且每个叶节点连接到其相邻的叶节点。这有以下几个好处:

  1. 双向链表允许在页之间快速移动,便于在不同页之间进行导航。这对于遍历和访问数据时非常高效,因为可以在链表中向前和向后移动。
  2. 在处理行插入和删除时,双向链表可以快速找到需要更新的页。这对于页的合并、分裂和重用都非常有利。
  3. 执行范围查询不需要扫描整个树。它所需要做的就是找到包含最小值的叶节点,然后加载下一个叶节点,以此类推,直到到达包含最大值的叶结点。

如果以随机的 key 顺序插入记录,会有性能问题。例如,添加一条键为13的记录,MySQL会将该记录保存在第8页。但是,当您使用 9 作为 key 添加记录时,会导致第7页进行拆分。在这种情况下,MySQL会在页之间移动记录并影响性能。

非聚集索引

辅助索引类似于聚集索引,区别在于,它不在叶子节点中存储数据行,而只存储辅助索引的索引 key 和聚集索引的 key (通常是主键)来查找记录:
在这里插入图片描述
辅助索引 key 的顺序和聚集索引 key 的顺序通常不相同,因此进行范围查询不如聚集索引有效。此外,由于辅助索引将存储聚集索引 key ,聚集索引 key 的大小将影响辅助索引的大小。

索引维护

MySQL不会填满整个页的空间;相反,它会为以后的修改留下空间,比如上图第6页。根据MySQL 5.7参考手册:

当新记录插入到InnoDB聚集索引中时,InnoDB会尝试留出1/16的页空间,以便将来插入和更新索引记录。如果按顺序插入索引记录,升序或降序,则生成的索引页的大小约为15/16。如果记录以随机顺序插入,则页面的大小1/2 ~ 15/16。

顺序插入和随机插入

插入数据的顺序取决于多个因素,包括表的类型、存储引擎、索引设计以及具体的插入操作。以下是一些关键情况:

  1. 顺序插入
    自增主键:使用自增(AUTO_INCREMENT)字段时,插入的记录通常是顺序的。例如,当你插入新的行时,它们会根据自增值的顺序添加到表中。
    没有竞争的插入:在没有并发插入的情况下,数据通常按插入的顺序存储。
    InnoDB 存储引擎:InnoDB 使用聚簇索引(Clustered Index),数据行按照主键顺序存储,因此在自增主键的情况下,数据会顺序插入。
  2. 随机插入
    非自增主键:如果你使用非自增主键(如 UUID 等),插入的记录可能会在不同的位置随机存储。
    并发插入:在高并发环境下,多个并发插入可能会导致数据在存储中的位置不再是顺序的。
    使用特定的插入策略:如果你在插入时指定了一个具体的值(而不是自增),并且这些值是随机生成的,那么数据将随机分布在表中。
    表类型:
    对于某些表类型(如 MyISAM),如果插入数据时没有索引,数据可能会顺序插入。然而,一旦有索引,插入可能会导致随机位置的更新。
  3. 影响因素
    事务:在事务中,数据的插入顺序可能会因为锁机制而影响。
    索引更新:当插入新记录时,索引的更新可能导致数据在存储中的位置分散。
    数据分区:如果使用了数据分区,插入数据的顺序也可能受到影响。

总结
顺序插入:通常发生在使用自增主键且没有并发插入的情况下。
随机插入:发生在使用非自增主键、并发插入或特定插入策略的情况下。

为什么预留空间

在 MySQL 中,页是存储引擎用于管理数据的基本单位。预留空间在 MySQL 页管理中起着关键作用。预留空间是为了提高性能和效率,它能够减少页分裂,提高插入和更新性能,优化空间利用率,并提升整体数据库的并发处理能力和查询性能。以下是一些主要原因:

  1. 减少页分裂频率
    当页满时,如果需要插入新数据,存储引擎会执行页分裂,导致性能下降。预留空间可以减少这种情况的发生,从而减少页分裂的频率。
  2. 提高插入性能
    在批量插入数据时,预留空间可以容纳更多的数据,减少因频繁分裂页而导致的性能开销。
  3. 优化更新操作
    行更新:当更新现有行的大小时,例如增加字符串长度,预留空间可以避免频繁的页移动和分裂,减少锁争用和提高并发性能。
  4. 提高空间利用率
    预留空间有助于维持高效的空间利用率,避免频繁的页合并和碎片化,保持数据的紧凑存储。
  5. 支持动态行格式
    动态扩展:例如,InnoDB 支持动态行格式,长行数据可以存储在不同的页中,预留空间有助于管理这些指针和数据的存储。
  6. 减轻锁争用
    提高并发性:预留空间可以减少因页分裂导致的锁争用,从而提高数据库的并发处理能力。
  7. 优化读性能
    缓存效果:预留的空间可以提高页的缓存效果,减少磁盘 IO,从而提高查询性能。

索引构建过程

https://dev.mysql.com/doc/refman/8.4/en/sorted-index-builds.html
InnoDB在创建或重建B+树索引时执行批量加载。这种索引创建方法被称为排序索引构建。innodb_fill_factor变量定义了在排序索引构建期间填充的每个B+树页面上的空间百分比,剩余空间保留用于未来的索引增长。innodb_fill_factor设置为100时,聚集索引页中有1/16的空间可用于未来的索引增长。

索引构建有三个阶段:

  1. 第一阶段,扫描聚集索引,生成索引项并将其添加到排序缓冲区。当排序缓冲区已满时,索引项将被排序,然后写入临时中间文件
  2. 第二阶段,第一阶段执行了一次或多次后,对写入的临时中间文件中的所有索引项执行合并排序
  3. 第三阶段,将排序后的索引项插入到B+树中;这个过程是多线程的

页的合并拆分

页满率:页的数据量占总数据容量的百分比

如果删除行或通过UPDATE操作缩短行时索引页的页满率低于MERGE_THRESHOLD值,InnoDB将尝试将索引页与相邻索引页进行合并。默认的MERGE_THRESHOLD值为50,最小MERGE_THRESHOLD值为1,最大值为50。

当索引页的页满率降至默认MERGE_THRESHOLD设置的50%以下时,InnoDB会尝试将索引页与相邻页面合并。如果两个页面都接近50%满,则页面合并后很快就会发生页面拆分。如果这种合并-拆分行为频繁发生,可能会对性能产生不利影响。为了避免频繁的合并拆分,您可以降低MERGE_THRESHOLD值,以便InnoDB尝试以较低的容量百分比进行页合并。以较低的页满率合并页会在索引页面中留下更多空间,并有助于减少页的合并拆分行为。

索引页的MERGE_THRESHOLD可以为表或单个索引定义。为单个索引定义的MERGE_THRESHOLD值优先于为表定义的MERGE_THRESHORD值。如果未定义,则MERGE_THRESHOLD值默认为50。


参考资料:

  1. https://dev.mysql.com/doc/refman/8.4/en/innodb-physical-structure.html
  2. https://medium.com/@genchilu/a-brief-introduction-to-cluster-index-and-secondary-index-in-innodb-9b8874d4da6a

http://www.kler.cn/news/324437.html

相关文章:

  • 【分布式微服务云原生】8分钟掌握微服务通信的艺术:Dubbo与OpenFeign全面解析
  • MacOS上安装MiniConda的详细步骤
  • SVG 滤镜:探索图形设计的无限可能
  • 低代码可视化-UniApp二维码可视化-代码生成器
  • C#测试调用FreeSpire.PDFViewer浏览PDF文件
  • 浅谈C++之Redis缓存
  • 遥感图像变换检测实践上手(TensorRT+UNet)
  • 一文理解mysql 联合索引和各种SQL语句分析
  • 软考论文《论模型驱动架构设计方法及其应用》精选试读
  • Python邮件发送附件:怎么配置SMTP服务器?
  • Java 常用运算符简单总结
  • JS中的事件和DOM操作
  • 【表达式求值算法】拆解复杂问题:实现计算器
  • 17.第二阶段x86游戏实战2-线程发包和明文包
  • Python近红外光谱数据分析
  • 几个将ppt文件压缩变小的方法!
  • [CKA]CKA预约和考试
  • 产品包装检测系统源码分享
  • OpenGL ES简述(1)
  • 如何使用 WebRTC 获取摄像头视频
  • 组播基础-2-IGMP协议
  • ★ C++进阶篇 ★ map和set
  • 个人健康管理小程序(源码+参考文档+定制)
  • python中序列化和反序列化
  • 一步一步优化一套生成式语言模型系统
  • docker简介、安装、基础知识
  • 基于webComponents的纯原生前端框架
  • Xcode 16 上传AppStore遇到第三方库 bitcode 的问题
  • Python爬虫bs4基本使用
  • Java编程基础:类与对象的探索之旅