深入了解MySQL索引结构

本篇文章给大家带来了关于mysql的相关知识,其中主要介绍了关于索引结构的相关问题,那么,索引的结构是什么样的?为什么索引可以这么快?下面一起来看一下吧,希望对大家有帮助。

深入了解MySQL索引结构

推荐学习:mysql教程

数据库存储单位

首先我们要知道,由于为了实现持久化,只能将索引存储在硬盘上,通过索引来进行查询的时候就会产生硬盘的 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, 表示每个节点最多能有多少个子节点),

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

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

纳米搜索 纳米搜索

纳米搜索:360推出的新一代AI搜索引擎

纳米搜索 30 查看详情 纳米搜索

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

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

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

B+ 树索引

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

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

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

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

B+ 树优点:

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

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 索引有一些局限性,因此不是最主流的索引结构。

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

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

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

推荐学习:mysql教程

以上就是深入了解MySQL索引结构的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/283569.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年11月4日 17:34:33
下一篇 2025年11月4日 17:35:40

相关推荐

  • Golang中如何使用goroutine实现一个简单的定时任务调度器

    答案:通过goroutine和channel实现并发定时任务调度,利用time.Ticker精确控制执行间隔,结合context.Context实现优雅启动、停止及单个任务取消,确保并发安全与资源释放,为后续扩展cron表达式、持久化、分布式等高级功能奠定基础。 在Golang中,利用其原生的gor…

    2025年12月15日
    000
  • Golang中非main包里的init函数会按照什么顺序执行

    init函数按依赖关系自底向上执行,同一包内按文件编译顺序执行;循环依赖会导致编译错误;init中panic会终止程序启动;应避免复杂逻辑以提升可维护性。 Golang中,非 main 包的 init 函数执行顺序并非完全线性,它受到包的导入关系和文件编译顺序的影响。简单来说,它会按照依赖关系自底向…

    2025年12月15日
    000
  • Golang实现简单爬虫怎么做 组合net/http与goquery解析HTML

    答案:使用Golang实现爬虫需先用net/http发送请求并处理错误、超时和重定向,再通过goquery结合CSS选择器解析HTML提取数据,最后利用goroutine和channel实现并发抓取,配合WaitGroup同步,数据可存为文件或数据库。 用Golang实现一个简单的爬虫,核心思路其实…

    2025年12月15日
    000
  • Golang实现短链接服务 算法与存储设计

    短链接服务核心是唯一标识生成与高效存储。采用“分布式ID+Base62编码”算法可保证唯一性与较短长度,结合“MySQL/PostgreSQL+Redis”存储架构,利用Redis缓存高频读取,数据库持久化保证一致性,Golang通过goroutine处理高并发,配合连接池、异步队列与监控实现高性能…

    2025年12月15日
    000
  • Google App Engine Go 应用中的状态管理与持久化策略

    本文旨在解决Google App Engine (GAE) Go 应用中因实例自动伸缩导致的内存变量重置问题。当GAE启动新进程时,应用内存中的数据会丢失。核心解决方案是避免将关键数据存储在RAM中,而应利用GAE提供的持久化存储服务,如Memcache、Datastore等,以确保数据在不同实例间…

    2025年12月15日
    000
  • Golang错误处理与数据库操作 SQL错误转换技巧

    答案:Go中数据库错误处理需通过errors.As提取底层错误并结合SQL状态码进行精准转换,避免依赖错误消息字符串。应封装统一的错误映射函数,将驱动错误(如PostgreSQL的23505唯一键冲突)转化为应用级错误,提升代码健壮性与可维护性。 在Go语言开发中,错误处理和数据库操作是两个高频且关…

    2025年12月15日
    000
  • Golang初级项目完整指南 从零到上线

    对于初学者来说,从零开始搭建并成功上线一个Go语言项目,关键在于理解其简洁高效的特性,并遵循一套从概念到部署的实践路径。这不仅仅是写几行代码,更是一次系统性思考和解决问题的过程,涵盖了从项目初始化、依赖管理、核心逻辑开发、测试到最终部署上线的全链路。 解决方案 要将一个Go语言初级项目从零带到线上,…

    2025年12月15日
    000
  • Golang如何集成数据库开发环境 常见数据库驱动配置

    首先引入database/sql标准库和对应数据库驱动,如MySQL的github.com/go-sql-driver/mysql;通过sql.Open()使用DSN连接数据库,需正确配置用户名、密码、地址等信息;导入驱动时使用下划线表示仅执行初始化注册;成功获取*sql.DB实例后,应设置连接池参…

    2025年12月15日
    000
  • Golang网络编程中的连接池管理 对比不同连接池实现方案

    连接池能显著提升性能和资源利用率。在没有连接池时,每次请求需新建并关闭tcp连接,耗时且易导致资源限制问题;使用连接池后可复用连接,减少开销,并控制最大连接数防止资源耗尽。常见连接池库包括database/sql(适合数据库场景但配置有限)、net/http transport(内置http连接复用…

    2025年12月15日 好文分享
    000
  • Golang实现云原生数据库代理 分库分表中间件开发

    答案:基于Golang构建云原生数据库代理需集成SQL解析、路由引擎、连接池与结果合并模块,选用vitess或TiDB解析器,支持分库分表路由策略,结合Kubernetes实现服务发现与弹性伸缩,通过Prometheus监控保障稳定性。 要用 Golang 实现云原生数据库代理,并支持分库分表,核心…

    2025年12月15日
    000
  • Golang模块如何支持多数据库驱动 讲解database/sql解耦设计实践

    golang项目支持多数据库驱动的关键在于利用database/sql标准库的解耦设计。其核心方法包括:1. 接口抽象,通过统一接口实现业务逻辑与具体数据库解耦;2. 驱动注册机制,通过匿名导入驱动包并在运行时动态选择数据库类型;3. 项目结构分层,定义统一dao接口、为不同数据库编写适配器并根据配…

    2025年12月15日 好文分享
    000
  • Golang连接MySQL数据库 database/sql使用指南

    答案:使用database/sql包和go-sql-driver/mysql驱动连接MySQL,需正确配置DSN(含charset、parseTime、loc等参数)以避免乱码和时间处理错误,合理设置连接池参数(MaxOpenConns、MaxIdleConns、ConnMaxLifetime)提升…

    2025年12月15日
    000
  • Golang多租户实现 数据库隔离方案

    独立数据库提供最高安全性,适合高合规场景但成本高;2. 共享数据库独立Schema平衡隔离与运维,适用于中等规模租户;3. 共享表通过tenant_id区分数据,资源高效但依赖代码严谨;4. 混合方案按租户等级灵活选择,结合中间件解析租户、GORM回调注入条件、上下文传递租户ID,确保数据隔离贯穿调…

    2025年12月15日
    000
  • 怎样用Golang实现工厂模式 对比简单工厂与抽象工厂区别

    简单工厂模式适用于创建单一类型的不同对象,通过一个工厂函数根据参数返回具体实现,适合产品种类少且变化不频繁的场景;抽象工厂模式则用于创建一组相关或依赖的对象家族,通过定义抽象工厂接口和多个具体工厂来保证产品间的一致性,适合需要整体切换产品族的复杂系统。两者核心区别在于简单工厂关注单个对象创建,抽象工…

    2025年12月15日
    000
  • Golang Web项目架构 分层设计最佳实践

    分层设计通过职责分离提升Go Web项目的可维护性与可测试性,典型模式为Handler→Service→Repository→Model四层架构,各层通过接口解耦并依赖注入实现低耦合,便于测试、协作与扩展。 在构建Golang Web项目时,采用分层设计是确保项目可维护、可扩展和易于测试的关键。它本…

    2025年12月15日
    000
  • 如何在Golang中处理CSV大文件 介绍csv.Reader流式处理与内存管理

    在golang中处理csv大文件应使用流式处理,通过csv.reader逐行读取以避免内存暴涨。具体步骤包括:1. 使用csv.newreader配合os.open按行读取文件;2. 避免累积数据、及时释放引用、使用指针传递结构体、合理设置缓冲区以控制内存;3. 推荐边读边写或分批处理,如每读100…

    2025年12月15日 好文分享
    000
  • 怎样测试Golang的数据库操作 使用测试容器与mock方案

    测试Golang数据库操作需隔离外部依赖,常用测试容器和Mock框架。2. 测试容器如Testcontainers启动真实数据库做集成测试,验证SQL、事务等真实行为。3. 示例使用testcontainers-go启动PostgreSQL,初始化表结构,执行CRUD并验证结果。4. Mock框架如…

    2025年12月15日
    000
  • Golang云原生数据库代理 分片中间件

    基于Golang的云原生数据库分片中间件通过SQL解析与路由、连接池管理、结果合并、读写分离及高可用机制,实现数据库水平拆分;利用Golang高性能网络编程、丰富SQL解析库和云原生集成优势,结合协议解析层、路由引擎、元数据管理、执行引擎和监控组件,构建高效可扩展架构,参考Vitess等开源项目,支…

    2025年12月15日
    000
  • Go语言中SQL数据库访问:database/sql 包与驱动生态

    Go语言通过其标准库中的database/sql包提供了一套统一的SQL数据库访问接口。该包定义了通用的数据库操作规范,而具体的数据库连接与操作则由遵循其driver接口的第三方驱动实现。这种设计模式确保了Go在数据库操作上的灵活性、可扩展性和高性能,使其能够广泛应用于各类任务关键型应用,而非仅限于…

    2025年12月15日
    000
  • Golang构建云原生监控工具 Prometheus Exporter开发

    云原生环境中,监控是保障系统稳定运行的关键环节。Prometheus 作为主流的开源监控系统,通过拉取模式采集指标数据,广泛应用于 Kubernetes、微服务架构等场景。而 Go 语言(Golang)凭借其高并发、低延迟和静态编译的特性,成为开发 Prometheus Exporter 的理想选择…

    2025年12月15日
    000

发表回复

登录后才能评论
关注微信