MySQL索引数据结构深度解析:B+Tree vs Hash

MySQL索引数据结构深度解析:B+Tree vs Hash

MySQL索引数据结构深度解析:B+Tree vs Hash

🌺The Begin🌺点点关注,收藏不迷路🌺

引言

当我们谈论MySQL索引时,本质上是在讨论数据结构。就像不同的工具箱适合不同的修理场景一样,不同的索引数据结构也各有优劣。MySQL主要支持两种索引数据结构:B+TreeHash。本文将深入剖析这两种结构的原理、优缺点以及适用场景,并用流程图和代码示例帮助你彻底理解。

1. 索引数据结构全景图

MySQL索引数据结构

索引数据结构

B+Tree索引

Hash索引

InnoDB默认

MyISAM默认

适合范围查询

适合排序

Memory默认

适合等值查询

不适合范围查询

不适合排序

2. B+Tree索引详解

2.1 什么是B+Tree?

B+Tree是一种平衡多路查找树,是B-Tree的变种。它是MySQL InnoDB和MyISAM存储引擎默认的索引结构。

2.2 B+Tree的结构特点

B+Tree结构示意图

双向链表

双向链表

双向链表

双向链表

双向链表

根节点
存储索引项
50 100 150

中间节点
20 35 50

中间节点
70 85 100

中间节点
120 135 150

叶子节点
10 15 20

叶子节点
25 30 35

叶子节点
40 45 50

叶子节点
60 65 70

叶子节点
75 80 85

叶子节点
90 95 100

B+Tree的核心特性

特性说明
多路平衡每个节点可以有多个子节点,树始终保持平衡
非叶子节点只存索引不存储数据,只存储索引键值和指向子节点的指针
叶子节点存数据所有数据都存储在叶子节点
叶子节点有序叶子节点按索引键值顺序排列
叶子节点双向链表叶子节点之间有指针相连,方便范围查询

2.3 B+Tree vs B-Tree

B+Tree

节点
10 20 30
指针

节点
5 8
指针

节点
15 18
指针

节点
25 28
指针

叶子节点
1 3 5 8
data

叶子节点
10 12 15 18
data

叶子节点
20 22 25 28
data

B-Tree

节点
10 20 30
data1 data2 data3

节点
5 8
data

节点
15 18
data

节点
25 28
data

对比维度B-TreeB+Tree
非叶子节点存储索引+数据只存储索引
叶子节点存储数据存储数据+双向链表
范围查询中序遍历链表直接遍历
树的高度较高较低(扇出大)
磁盘IO较多较少

2.4 B+Tree的查询过程

-- 示例:查找 age = 45 的记录-- 表结构CREATETABLEuser( id INTPRIMARYKEY, name VARCHAR(50), age INT,INDEX idx_age (age));-- 查询语句SELECT*FROMuserWHERE age =45;

查询步骤

  1. 根节点:加载根节点,比较45,确定去哪个子节点
  2. 中间节点:逐层向下,直到叶子节点
  3. 叶子节点:在叶子节点中找到age=45的记录
  4. 回表(如果需要):如果查询字段不都在索引中,通过主键回表取完整数据

数据表叶子节点中间节点根节点客户端数据表叶子节点中间节点根节点客户端查找age=4545>30,走右子树进入中间节点45<60,走左子树进入叶子节点在有序链表中找到45根据主键id回表返回完整数据

2.5 B+Tree的三大优势

优势一:范围查询高效

-- B+Tree范围查询SELECT*FROMuserWHERE age BETWEEN20AND30;-- 执行过程-- 1. 找到age=20的第一个记录-- 2. 沿着叶子节点的链表向后遍历-- 3. 直到遇到第一个age>30的记录停止

优势二:排序无需额外操作

-- B+Tree排序查询SELECT*FROMuserORDERBY age LIMIT10;-- 执行过程-- 1. 直接遍历最左边的叶子节点-- 2. 顺序读取即可,无需filesort

优势三:磁盘IO少

假设1000万条数据,B+Tree高度约为3-4 查询一条数据最多需要3-4次磁盘IO 每次IO约10ms,总耗时30-40ms 

2.6 B+Tree的适用场景

场景说明示例
范围查询BETWEEN、>、<、>=、<=WHERE age BETWEEN 20 AND 30
排序查询ORDER BYORDER BY create_time
分组查询GROUP BYGROUP BY category
模糊匹配前缀LIKE ‘prefix%’WHERE name LIKE '张%'
全值匹配等值查询WHERE id = 100

3. Hash索引详解

3.1 什么是Hash索引?

Hash索引基于哈希表实现,通过哈希函数将索引键值计算为哈希值,映射到对应的槽位。MySQL中只有Memory存储引擎默认支持Hash索引。

3.2 Hash索引的结构

Hash索引结构

键值: '张三'

Hash函数
CRC32

哈希值: 0x7F3A5C

键值: '李四'

Hash函数

哈希值: 0x2B8D1E

键值: '王五'

Hash函数

哈希值: 0x7F3A5C
哈希冲突

槽位0x7F3A5C

槽位0x2B8D1E

链表解决冲突
'张三' -> '王五'

3.3 Hash索引的查询过程

-- 示例:查找 name = '张三' 的记录-- 表结构(Memory引擎)CREATETABLEuser( id INT, name VARCHAR(50), age INT,INDEX idx_name USINGHASH(name))ENGINE=MEMORY;-- 查询语句SELECT*FROMuserWHERE name ='张三';

查询步骤

  1. 计算哈希值:对’张三’应用哈希函数,得到哈希值H
  2. 定位槽位:根据H找到对应的槽位
  3. 处理冲突:如果槽位有多个记录,遍历链表
  4. 返回数据:找到匹配的记录直接返回(Memory引擎数据在内存中)

3.4 Hash索引的优缺点

优点说明
查询极快时间复杂度O(1),一次计算直接定位
结构简单实现容易,占用空间小
等值查询最优唯一索引的等值查询效率最高
缺点说明
不支持范围查询无法使用>、<、BETWEEN
不支持排序哈希值不保留原始大小关系
不支持部分索引必须使用全部索引列
哈希冲突多个键映射到同一槽位,影响性能
不支持模糊匹配LIKE查询无法使用

3.5 Hash索引的局限性演示

-- 1. 不支持范围查询EXPLAINSELECT*FROMuserWHERE age >30;-- type: ALL (全表扫描,无法使用Hash索引)-- 2. 不支持排序EXPLAINSELECT*FROMuserORDERBY age;-- Extra: Using filesort (无法使用Hash索引)-- 3. 不支持部分索引-- 联合索引 (a,b),查询条件只有aEXPLAINSELECT*FROM t WHERE a =1;-- 无法使用Hash索引,必须同时提供a和b-- 4. 不支持模糊查询EXPLAINSELECT*FROMuserWHERE name LIKE'张%';-- 无法使用Hash索引

4. B+Tree vs Hash 对比

4.1 全方位对比表

对比维度B+Tree索引Hash索引
数据结构平衡多路树哈希表
时间复杂度O(log N)O(1)
等值查询极快
范围查询支持不支持
排序查询支持不支持
模糊查询支持前缀匹配不支持
联合索引支持最左前缀必须全列匹配
磁盘IO较多(树的高度)较少(直接定位)
存储引擎InnoDB、MyISAM等Memory
适用场景大多数OLTP场景等值查询为主的场景

4.2 查询性能对比图

范围查询

B+Tree: 链表遍历

线性增长

Hash: 不支持

全表扫描

等值查询

B+Tree: 3-4次IO

约30-40ms

Hash: 1次计算

约1-2ms

4.3 选择建议

/** * 索引类型选择决策器 */publicclassIndexTypeChooser{/** * 根据查询模式推荐索引类型 */publicstaticStringrecommendIndexType(QueryPattern queryPattern){// 场景1:只有等值查询,没有范围查询if(queryPattern.isOnlyEqualityQueries()){return"可以考虑Hash索引(如果使用Memory引擎)";}// 场景2:包含范围查询或排序if(queryPattern.hasRangeQueries()|| queryPattern.hasOrderBy()){return"必须使用B+Tree索引";}// 场景3:模糊查询if(queryPattern.hasLikeQueries()){return"必须使用B+Tree索引(支持前缀匹配)";}// 默认推荐return"B+Tree索引(适用性最广)";}}// 查询模式类classQueryPattern{privateList<String> queryTypes;publicbooleanisOnlyEqualityQueries(){return queryTypes.stream().allMatch(t -> t.equals("EQ"));}publicbooleanhasRangeQueries(){return queryTypes.stream().anyMatch(t -> t.equals("GT")|| t.equals("LT")|| t.equals("BETWEEN"));}publicbooleanhasOrderBy(){return queryTypes.contains("ORDER_BY");}publicbooleanhasLikeQueries(){return queryTypes.contains("LIKE");}}

5. 实际案例:索引选择错误导致的性能问题

5.1 错误案例:使用Hash索引做范围查询

-- 错误设计CREATETABLE event_log ( id INTPRIMARYKEY, event_time DATETIME, content VARCHAR(255),INDEX idx_time USINGHASH(event_time)-- 错误:Hash索引不能范围查询)ENGINE=MEMORY;-- 查询某时间段日志SELECT*FROM event_log WHERE event_time BETWEEN'2024-01-01'AND'2024-01-02';-- 结果:全表扫描,性能极差

5.2 正确设计

-- 正确设计CREATETABLE event_log ( id INTPRIMARYKEY, event_time DATETIME, content VARCHAR(255),INDEX idx_time (event_time)-- 默认B+Tree)ENGINE=InnoDB;-- 查询某时间段日志SELECT*FROM event_log WHERE event_time BETWEEN'2024-01-01'AND'2024-01-02';-- 结果:使用索引,快速返回

6. 面试题:为什么MySQL选择B+Tree作为默认索引?

6.1 面试官考察点

考察点回答要点
数据结构理解B+Tree的特点:多路、平衡、叶子链表
业务场景考虑MySQL需要支持范围查询、排序
磁盘IO优化B+Tree高度低,减少磁盘IO
对比分析与B-Tree、Hash对比优势

6.2 标准答案

B+Tree作为MySQL默认索引结构,主要基于以下原因:

  1. 适合磁盘存储:B+Tree每个节点可以存储大量索引项,树的高度低(通常3-4层),查询时磁盘IO次数少
  2. 支持范围查询:叶子节点形成双向链表,范围查询时只需找到起点,然后沿着链表遍历,效率极高
  3. 支持排序:数据按索引键值顺序存储,ORDER BY可以直接使用索引,无需额外排序
  4. 查询稳定:任何查询从根节点到叶子节点的路径长度相同,性能稳定
  5. 适应MySQL的OLTP场景:大多数业务场景既需要等值查询,也需要范围查询和排序

7. 总结

7.1 一句话总结

  • B+Tree索引:像一本有目录和页码的书,既能精确查找,又能连续阅读
  • Hash索引:像一本词典后的部首检字表,定位单个字极快,但不能连续查看

7.2 选择指南

场景推荐索引原因
大多数业务系统B+Tree适用性最广
缓存表、临时表Hash等值查询为主
日志分析系统B+Tree需要范围查询
排行榜系统B+Tree需要排序

最终结论:B+Tree是MySQL的默认选择,也是最稳妥的选择。Hash索引只在特定场景(Memory引擎、纯等值查询)下使用。理解两者的原理和适用场景,才能在设计索引时做出正确的选择。


🌺The End🌺点点关注,收藏不迷路🌺

Read more

【前端实战】从 try-catch 回调到链式调用:一种更优雅的 async/await 错误处理方案

【前端实战】从 try-catch 回调到链式调用:一种更优雅的 async/await 错误处理方案

目录 【前端实战】从 try-catch 回调到链式调用:一种更优雅的 async/await 错误处理方案 一、问题背景:async/await 真的解决了一切麻烦吗? 二、真实业务场景下的痛点 1、错误需要“分阶段处理” 2、try-catch 的引入打破了 async/await 的链式范式 三、借鉴 Go、Rust 语言特性,错误也是一种结果 1、错误优先风格替代 try-catch 2、封装一个 safeAsync 工具函数 四、进阶版 safeAsync 函数设计 五、结语         作者:watermelo37         ZEEKLOG优质创作者、华为云云享专家、阿里云专家博主、腾讯云“

By Ne0inhk
湖南首条免费高速轨迹呈现:借助 Leaflet -Trackplayer 实现 WebGIS 可视化

湖南首条免费高速轨迹呈现:借助 Leaflet -Trackplayer 实现 WebGIS 可视化

目录 前言 一、相关背景 1、湖南首条免费高速-长永高速 2、还有哪些快到30年的高速 3、leaflet-trackplayer相关知识 二、基础数据准备 1、高速起止点地理编码 2、途径重要AOI和POI信息 3、高速区间道路信息 三、leaflet-trackplayer实战 1、行驶道路生成和设置 2、途径重要AOI和POI 3、车辆车牌信息模拟跟随 4、成果展示 四、总结 前言         在交通基础设施建设与数字化技术飞速发展的时代,湖南迎来了其首条免费高速公路的建成通车,这不仅是交通领域的一大突破,更是区域经济发展与民生改善的重要里程碑。然而,如何更好地展示这条高速公路的运行轨迹,为交通管理、规划以及公众出行提供直观,成为了我们亟待解决的问题。将WebGIS 技术与 Leaflet - Trackplayer 的结合,为我们提供了一种创新且高效的解决方案。WebGIS(Web 地理信息系统)

By Ne0inhk

Flutter 三方库 xpath_selector 的鸿蒙化适配指南 - 在鸿蒙系统上构建极致、透明、精准的 HTML/XML 数据抓取与 Web 结构解析引擎

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 xpath_selector 的鸿蒙化适配指南 - 在鸿蒙系统上构建极致、透明、精准的 HTML/XML 数据抓取与 Web 结构解析引擎 在鸿蒙(OpenHarmony)系统的网络爬虫、自动化测试审计、或者是从复杂的第三方 Web 公告(HTML)中提取关键数据(如新闻标题、资产负债表)时,如何摆脱凌乱的正向正则(Regex),转而使用业界标准的 XPath 语法进行语义化选取?xpath_selector 为开发者提供了一套工业级的、基于 Dart 的 HTML/XML 结构化查询方案。本文将深入实战其在鸿蒙端数据治理中的应用。 前言 什么是 XPath Selector?

By Ne0inhk
.NET 的 WebApi 项目必要可配置项都有哪些?

.NET 的 WebApi 项目必要可配置项都有哪些?

目录 一、数据库配置 (一)选择合适的数据库提供程序 (二)配置数据库连接字符串 (三)数据库迁移(以 EF Core 为例) 二、依赖注入配置 (一)理解依赖注入 (二)注册服务 (三)使用依赖注入 三、Swagger 配置 (一)安装 Swagger 相关包 (二)配置 Swagger 服务 (三)启用 Swagger 中间件 四、接口接收和输出大小写配置 (一)接口接收大小写配置 (二)接口输出大小写配置 五、跨域配置 (一)什么是跨域 (二)配置跨域 六、身份验证与授权配置

By Ne0inhk