MySQL B+树索引(BTREE)和哈希索引(HASH)的区别

本文深入解析MySQL中的BTree和Hash索引,探讨它们的工作原理、适用场景及性能考量,为数据库优化提供指导。

索引是帮助mysql获取数据的数据结构。

最常见的索引是Btree索引和Hash索引。

不同的引擎对于索引有不同的支持:Innodb和MyISAM默认的索引是Btree索引;而Mermory默认的索引是Hash索引。

我们在mysql中常用两种索引算法BTree和Hash,两种算法检索方式不一样,对查询的作用也不一样。

其他相关文章:MySQL B+树索引和哈希索引的区别

一、BTree
BTree索引是最常用的mysql数据库索引算法,因为它不仅可以被用在=,>,>=,<,<=和between这些比较操作符上,而且还可以用于like操作符,只要它的查询条件是一个不以通配符开头的常量,例如:
select * from user where name like ‘jack%’;
select * from user where name like ‘jac%k%’;
如果一通配符开头,或者没有使用常量,则不会使用索引,例如:
select * from user where name like ‘%jack’;
select * from user where name like simply_name;
二、Hash
Hash索引只能用于对等比较,例如=,<=>(相当于=)操作符。由于是一次定位数据,不像BTree索引需要从根节点到枝节点,最后才能访问到页节点这样多次IO访问,所以检索效率远高于BTree索引。
但为什么我们使用BTree比使用Hash多呢?主要Hash本身由于其特殊性,也带来了很多限制和弊端:
1. Hash索引仅仅能满足“=”,“IN”,“<=>”查询,不能使用范围查询。
2. 联合索引中,Hash索引不能利用部分索引键查询。
对于联合索引中的多个列,Hash是要么全部使用,要么全部不使用,并不支持BTree支持的联合索引的最优前缀,也就是联合索引的前面一个或几个索引键进行查询时,Hash索引无法被利用。
3. Hash索引无法避免数据的排序操作
由于Hash索引中存放的是经过Hash计算之后的Hash值,而且Hash值的大小关系并不一定和Hash运算前的键值完全一样,所以数据库无法利用索引的数据来避免任何排序运算。
4. Hash索引任何时候都不能避免表扫描
Hash索引是将索引键通过Hash运算之后,将Hash运算结果的Hash值和所对应的行指针信息存放于一个Hash表中,由于不同索引键存在相同Hash值,所以即使满足某个Hash键值的数据的记录条数,也无法从Hash索引中直接完成查询,还是要通过访问表中的实际数据进行比较,并得到相应的结果。
5. Hash索引遇到大量Hash值相等的情况后性能并不一定会比BTree高
对于选择性比较低的索引键,如果创建Hash索引,那么将会存在大量记录指针信息存于同一个Hash值相关联。这样要定位某一条记录时就会非常麻烦,会浪费多次表数据访问,而造成整体性能底下。

三、两种索引的优缺点

Hash索引

所谓Hash索引,当我们要给某张表某列增加索引时,将这张表的这一列进行哈希算法计算,得到哈希值,排序在哈希数组上。所以Hash索引可以一次定位,其效率很高,而Btree索引需要经过多次的磁盘IO,但是innodb和myisam之所以没有采用它,是因为它存在着好多缺点:

1、因为Hash索引比较的是经过Hash计算的值,所以只能进行等式比较,不能用于范围查询

1、每次都要全表扫描

2、由于哈希值是按照顺序排列的,但是哈希值映射的真正数据在哈希表中就不一定按照顺序排列,所以无法利用Hash索引来加速任何排序操作

3、不能用部分索引键来搜索,因为组合索引在计算哈希值的时候是一起计算的。

4、当哈希值大量重复且数据量非常大时,其检索效率并没有Btree索引高的。

Btree索引

至于Btree索引,它是以B+树为存储结构实现的。

但是Btree索引的存储结构在Innodb和MyISAM中有很大区别。

在MyISAM中,我们如果要对某张表的某列建立Btree索引的话,如图:

所以我们经常会说MyISAM中数据文件和索引文件是分开的。

因此MyISAM的索引方式也称为非聚集,Innodb的索引方式成为聚集索引。

至于辅助索引,类似于主索引,唯一区别就是主索引上的值不能重复,而辅助索引可以重复。

因此当我们根据Btree索引去搜索的时候,若key存在,在data域找到其地址,然后根据地址去表中查找数据记录。

至于Innodb它跟上面又有很大不同,它的叶子节点存储的并不是表的地址,而是数据

我们可以看到这里并没有将地址放入叶子节点,而是直接放入了对应的数据,这也就是我们平常说到的,Innodb的索引文件就是数据文件,

那么对于Innodb的辅助索引结构跟主索引也相差很多,如图:

(图源未知)

我们可以发现,这里叶子节点存储的是主键的信息,所以我们在利用辅助索引的时候,检索到主键信息,然后再通过主键去主索引中定位表中的数据,这就可以说明Innodb中主键之所以不宜用过长的字段,由于所有的辅助索引都包含主索引,所以很容易让辅助索引变得庞大。

我们还可以发现:在Innodb中尽量使用自增的主键,这样每次增加数据时只需要在后面添加即可,非单调的主键在插入时会需要维持B+tree特性而进行分裂调整,十分低效。

Btree索引中的最左匹配原则:
Btree是按照从左到右的顺序来建立搜索树的。比如索引是(name,age,sex),会先检查name字段,如果name字段相同再去检查后两个字段。

所以当传进来的是后两个字段的数据(age,sex),因为建立搜索树的时候是按照第一个字段建立的,所以必须根据name字段才能知道下一个字段去哪里查询。

所以传进来的是(name,sex)时,首先会根据name指定搜索方向,但是第二个字段缺失,所以将name字段正确的都找到后,然后才会去匹配sex的数据。

建立索引的规则:
1、利用最左前缀:Mysql会一直向右查找直到遇到范围操作(>,<,like、between)就停止匹配。比如a=1 and b=2 and c>3 and d=6;此时如果建立了(a,b,c,d)索引,那么后面的d索引是完全没有用到,当换成了(a,b,d,c)就可以用到。

2、不能过度索引:在修改表内容的时候,索引必须更新或者重构,所以索引过多时,会消耗更多的时间。

3、尽量扩展索引而不要新建索引

4、最适合的索引的列是出现在where子句中的列或连接子句中指定的列。

5、不同值较少的列不必要建立索引(性别)。

<think>我们正在讨论数据库索引类型中的BTree索引Hash索引区别。根据用户提供的引用材料,我们可以总结如下: 引用[1]指出:Hash索引只能用于对等比较(如=、>、<、>=、<=、between),并且由于一次定位数据,其检索效率高于BTree索引(因为BTree需要多次IO访问)。但同时也提出了疑问:为什么使用BTreeHash多? 引用[2]提供了详细的对比表格,从定义、使用引擎、核心区别、使用示例、使用建议总结对比等多个维度进行了分析。其中关键点包括: - 数据结构:BTree索引使用B+,而Hash索引使用哈希表。 - 是否有序:BTree有序,支持范围查询;Hash无序,不支持范围查询。 - 支持操作:BTree支持等值查询、范围查询、模糊匹配、排序等;Hash仅支持等值查询(=)。 - 查询效率:BTree为O(log n)Hash为O(1)(理想情况下)。 - 更新代价:BTree相对稳定,而Hash在更新频繁时可能导致哈希冲突。 - 联合索引BTree支持最左前缀匹配,Hash不支持。 引用[3]补充了Hash索引的缺点:无法进行范围查询(如<、>=、in)、无法排序、不能避免全表扫描(因为无法圈定范围)。 引用[4]再次强调Hash索引仅能满足"=""IN"等精确匹配,不能用于范围查询(如WHERE price>100),因为Hash值的大小关系与原始数据的大小关系不一定一致。 综合以上引用,我们可以系统地回答用户的问题: ### BTree索引Hash索引区别 #### 1. **数据结构** - **BTree索引**:基于B+结构(多路平衡查找),数据存储在有序的节点中,支持高效的范围查询排序。 - **Hash索引**:基于哈希表(数组+链表结构),通过哈希函数将键值映射到桶(bucket)位置,仅支持精确匹配[^2][^3]。 #### 2. **查询效率** - **BTree索引**:查询时间复杂度为$O(\log n)$,需要从根节点遍历到叶子节点(多次磁盘IO,但的高度较低,实际效率较高)[^1][^2]。 - **Hash索引**:理想情况下查询时间复杂度为$O(1)$,一次哈希计算即可定位数据(无磁盘IO,但需处理哈希冲突)[^1][^2]。 #### 3. **支持的操作** - **BTree索引**支持: - 等值查询(`=`) - 范围查询(`>`, `<`, `BETWEEN`) - 模糊匹配(`LIKE 'prefix%'`) - 排序(`ORDER BY`) - 最左前缀匹配(联合索引)[^2][^3][^4]。 - **Hash索引仅支持**: - 等值查询(`=`、`IN`)[^2][^3][^4]。 #### 4. **有序性** - **BTree索引**:数据按索引键有序存储(便于范围查询排序)[^2][^3]。 - **Hash索引**:数据无序存储(哈希值随机分布,无法保证顺序)[^2][^3]。 #### 5. **适用场景** - **BTree索引**适用于: - 需要范围查询(如`WHERE age > 18`) - 需要排序(如`ORDER BY create_time`) - 需要模糊匹配(如`LIKE 'Tom%'`) - 事务型存储引擎(如InnoDB,默认使用BTree)[^2][^3]。 - **Hash索引适用于**: - 仅等值查询(如缓存表) - 内存表(如MySQL的Memory引擎,默认使用Hash索引)[^2][^3]。 #### 6. **更新与冲突处理** - **BTree索引**:插入/删除时需维护平衡,但更新代价相对稳定(无冲突问题)[^2]。 - **Hash索引**:更新频繁时可能发生哈希冲突(需链地址法解决),性能可能退化[^2][^3]。 #### 7. **存储引擎支持** - **BTree索引**:被InnoDB、MyISAM等主流引擎支持[^2][^3]。 - **Hash索引**:仅Memory引擎默认支持(InnoDB不支持原生Hash索引,但可通过自适应哈希索引优化等值查询)[^2]。 ### 为什么BTree索引Hash索引更常用? 尽管Hash索引的等值查询效率更高($O(1)$),但实际数据库应用中范围查询、排序、模糊匹配等操作极为常见,而Hash索引无法支持这些操作。此外,BTree索引的有序性保证了查询的稳定性灵活性,使其成为通用场景的最优选择[^1][^2][^3][^4]。 --- ### 相关问题 1. 在哪些具体场景下应该优先选择Hash索引而非BTree索引? 2. MySQL的InnoDB引擎中自适应哈希索引(Adaptive Hash Index)是如何工作的? 3. 如何优化BTree索引以减少磁盘IO次数? 4. 联合索引BTreeHash索引中的实现机制有何不同?</think>### BTree索引Hash索引区别 #### 1. **数据结构与查询机制** - **BTree索引**: - 使用**B+结构**(多层级平衡) - 查询过程:从根节点→枝节点→叶节点逐层遍历(多次磁盘IO) - 时间复杂度:$O(\log n)$ - 示例:查找`age=25`需遍历节点定位数据[^2][^3] - **Hash索引**: - 使用**哈希表**(数组+链表结构) - 查询过程:通过哈希函数直接计算数据位置(一次定位) - 时间复杂度:$O(1)$(理想情况) - 示例:计算`name='Tom'`的哈希值直接访问存储桶[^1][^2] #### 2. **支持的操作类型** | **操作类型** | BTree索引 | Hash索引 | |--------------------|-----------|----------| | 等值查询(`=`, `IN`) | ✅ | ✅ | | 范围查询(`>`, `<`, `BETWEEN`) | ✅ | ❌ [^4] | | 模糊匹配(`LIKE 'T%'`) | ✅ | ❌ [^3] | | 排序(`ORDER BY`) | ✅ | ❌ [^3] | | 联合索引最左前缀匹配 | ✅ | ❌ [^2] | > 示例: > ```sql > -- Hash索引支持(等值查询) > SELECT * FROM users WHERE id = 100; > > -- BTree索引支持(范围查询+排序) > SELECT * FROM products WHERE price > 100 ORDER BY price; > ``` #### 3. **数据有序性与存储特性** - **BTree索引**: - 数据**严格有序**存储(叶节点形成有序链表) - 支持范围查询的核心原因(连续数据物理相邻) - 更新代价稳定(结构调整可控)[^2] - **Hash索引**: - 数据**完全无序**存储(哈希值随机分布) - 无法保证哈希值大小与原始数据大小的关系 (例如:`NULLIF(price, 100)`的哈希值无序)[^4] - 更新频繁时易发**哈希冲突**(需链地址法解决)[^2] #### 4. **适用场景对比** | **场景** | 推荐索引 | 原因说明 | |-------------------------|------------|------------------------------| | 纯等值查询(缓存表) | Hash索引 | $O(1)$查询效率最优 [^2] | | 范围查询/排序/模糊匹配 | BTree索引 | 有序性支持复杂操作 [^3] | | 事务型数据库(InnoDB) | BTree索引 | 默认索引类型,支持ACID [^2] | | 内存表(MEMORY引擎) | Hash索引 | 默认索引类型 [^2] | #### 5. **核心劣势对比** - **Hash索引的致命缺陷**: - ❌ 无法支持`WHERE price > 100`类范围查询[^4] - ❌ 无法实现`ORDER BY create_time`排序[^3] - ❌ 联合索引失效(如`(col1, col2)`需最左匹配)[^2] - ❌ 高冲突时性能退化(链表遍历降低$O(1)$优势) - **BTree索引的局限性**: - 空间占用稍大(需存储结构元数据) - 极端数据分布可能退化成$O(n)$(可通过平衡优化避免) #### 6. **实际应用案例** ```sql -- 用户管理系统(需要范围查询+排序) CREATE INDEX idx_age ON users(age) USING BTREE; -- 会话缓存表(仅需SessionID精确查找) CREATE INDEX idx_session ON sessions(session_id) USING HASH; ``` > **为什么BTree更常用?** > 真实业务中范围查询(如时间范围、价格区间)、排序(如排行榜)、模糊匹配(如搜索)等需求远多于纯等值查询,使BTree成为通用选择[^1][^3]。 --- ### 相关问题 1. InnoDB的自适应哈希索引(Adaptive Hash Index)如何结合BTreeHash的优点? 2. 如何通过EXPLAIN语句判断查询是否使用了Hash索引? 3. 在哪些特殊场景下Hash索引的性能会显著优于BTree索引? 4. 数据库如何处理Hash索引的冲突问题?链地址法对查询性能的影响如何量化?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值