Mysql B+树索引

本文介绍了MySQL中B+树索引的工作原理和重要性。从没有索引时的查找过程开始,逐步讲解了如何通过建立索引来提高查询效率。文章详细阐述了B+树的结构,包括叶子节点和非叶子节点的特性,以及如何通过B+树进行数据查找。还讨论了InnoDB存储引擎中的B+树索引方案,包括聚簇索引和二级索引的区别,并提到了B+树索引的一些注意事项,如根页面固定和内节点中目录项记录的唯一性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原文地址:https://amos-x.com/index.php/amos/archives/mysql-btree-index/

前言

索引是数据库提高查询效率的常用办法,那么索引为什么能提高查询效率,索引是怎么工作的,这就需要我们了解其本质上的工作原理,这也是很多面试时的常见问题。

下面我们就详细聊一下:索引。 在开始前,先说明一下,以下所有内容,基于默认的Innodb存储引擎,也是目前主流常用引擎。

正文

在开始聊索引前,首先需要明白:InnoDB是一个将表中的数据存储到磁盘上的存储引擎,且其将数据划分为若干个页,以页作为磁盘和内存之间交互的基本单位,InnoDB中页的大小一般为 16 KB。也就是在一般情况下,一次最少从磁盘中读取16KB的内容到内存中,一次最少把内存中的16KB内容刷新到磁盘中。且各个数据页可以组成一个双向链表,而每个数据页中的记录会按照主键值从小到大的顺序组成一个单向链表,每个数据页都会为存储在它里边儿的记录生成一个页目录,在通过主键查找某条记录的时候可以在页目录中使用二分法快速定位到对应的槽,然后再遍历该槽对应分组中的记录即可快速找到指定的记录。如下图:

image_1cov976plf2u1j3g1jp8serjc616.png-87.7kB

其中页a、页b、页c … 页n 这些页可以不在物理结构上相连,只要通过双向链表相关联即可。

Tips: 以上为基础知识,你可以不用完全懂,知道它是这样的就行,下面出现不太懂的相关知识页可以先看着,后面看着看着就理解了。如果有兴趣的,可以查看 掘金小册 的第5,6章节,详细了解其数据结构。好了,然后我们下面开始

没有索引的查找

本集的主题是索引,在正式介绍索引之前,我们需要了解一下没有索引的时候是怎么查找记录的。为了方便大家理解,我们下边先只唠叨搜索条件为对某个列精确匹配的情况,所谓精确匹配,就是搜索条件中用等于=连接起的表达式,比如这样:

SELECT [列名列表] FROM 表名 WHERE 列名 = xxx;

在一个页中的查找

假设目前表中的记录比较少,所有的记录都可以被存放到一个页中,在查找记录的时候可以根据搜索条件的不同分为两种情况:

  • 以主键为搜索条件这个查找过程我们已经很熟悉了,可以在页目录中使用二分法快速定位到对应的槽,然后再遍历该槽对应分组中的记录即可快速找到指定的记录。
  • 以其他列作为搜索条件对非主键列的查找的过程可就不这么幸运了,因为在数据页中并没有对非主键列建立所谓的页目录,所以我们无法通过二分法快速定位相应的。这种情况下只能从最小记录开始依次遍历单链表中的每条记录,然后对比每条记录是不是符合搜索条件。很显然,这种查找的效率是非常低的。

在很多页中查找

大部分情况下我们表中存放的记录都是非常多的,需要好多的数据页来存储这些记录。在很多页中查找记录的话可以分为两个步骤:

  1. 定位到记录所在的页。
  2. 从所在的页内中查找相应的记录。

在没有索引的情况下,不论是根据主键列或者其他列的值进行查找,由于我们并不能快速的定位到记录所在的页,所以只能从第一个页沿着双向链表一直往下找,在每一个页中根据我们刚刚唠叨过的查找方式去查找指定的记录。因为要遍历所有的数据页,所以这种方式显然是超级耗时的,如果一个表有一亿条记录,使用这种方式去查找记录那要等到猴年马月才能等到查找结果。所以祖国和人民都在期盼一种能高效完成搜索的方法,索引同志就要亮相登台了。

索引

为了故事的顺利发展,我们先建一个表:

mysql> CREATE TABLE index_demo(
    ->     c1 INT,
    ->     c2 INT,
    ->     c3 CHAR(1),
    ->     PRIMARY KEY(c1)
    -> ) ROW_FORMAT = Compact;
Query OK, 0 rows affected (0.03 sec)

这个新建的index_demo表中有2个INT类型的列,1个CHAR(1)类型的列,而且我们规定了c1列为主键,这个表使用Compact行格式来实际存储记录的。为了我们理解上的方便,我们简化了一下index_demo表的行格式示意图:

image_1caac8jr7hhcld017gd1lch1n1m33.png-97.1kB

我们只在示意图里展示记录的这几个部分:

  • record_type:记录头信息的一项属性,表示记录的类型,0表示普通记录、2表示最小记录、3表示最大记录、1我们还没用过,等会再说~
  • next_record:记录头信息的一项属性,表示下一条地址相对于本条记录的地址偏移量,为了方便大家理解,我们都会用箭头来表明下一条记录是谁。
  • 各个列的值:这里只记录在index_demo表中的三个列,分别是c1c2c3
  • 其他信息:除了上述3种信息以外的所有信息,包括其他隐藏列的值以及记录的额外信息。

为了节省篇幅,我们之后的示意图中会把记录的其他信息这个部分省略掉,因为它占地方并且不会有什么观赏效果。另外,为了方便理解,我们觉得把记录竖着放看起来感觉更好,所以将记录格式示意图的其他信息去掉并把它竖起来的效果就是这样:

image_1caacokob6ne1nv41meda0s7vk3g.png-68.1kB

把一些记录放到页里边的示意图就是:

image_1caadhc4g1pb7hk81fcd4vt1u6r3t.png-79.8kB

一个简单的索引方案

回到正题,我们在根据某个搜索条件查找一些记录时为什么要遍历所有的数据页呢?因为各个页中的记录并没有规律,我们并不知道我们的搜索条件匹配哪些页中的记录,所以 不得不 依次遍历所有的数据页。所以如果我们想快速的定位到需要查找的记录在哪些数据页中该咋办?还记得我们

<think>嗯,用户想比较MySQL中B+索引和哈希索引的优缺点及适用场景。首先,我需要回忆一下这两种索引的基本结构和工作原理。B+索引是一种多路平衡查找,而哈希索引则是基于哈希表实现的。两者的结构差异会导致不同的查询性能表现。 根据引用[3],B+索引适合范围查询和排序,而哈希索引在等值查询上更快。那用户可能需要知道什么情况下选择哪种索引更好。比如,如果应用中有很多范围查询,B+更合适;如果是精确查找,哈希可能更高效。 接下来,要考虑它们的存储结构。引用[4]提到B+的内部节点只存索引,不存数据,这样减少存储空间。而哈希索引可能需要处理哈希冲突,这会影响性能。这点需要详细说明。 性能对比方面,B+的查询时间复杂度是O(log n),哈希则是平均O(1),但最坏情况可能退化为O(n)。但实际中哈希冲突管理好的话,还是高效的。不过B+在范围查询时效率更高,这也是为什么MySQL默认使用B+的原因之一。 使用场景的话,用户可能需要知道何时选择哈希索引。比如内存表或者需要快速查找键值的场景。而B+适用于需要排序、分组或者范围查找的情况,比如订单时间范围查询。 另外,需要提到哈希索引不支持部分索引和覆盖索引,而B+支持,这点在优化查询时很重要。例如,如果查询只需要索引列,B+可以直接返回数据,而哈希需要回表。 还要注意MySQL的存储引擎支持情况,比如InnoDB的自适应哈希索引功能,用户可能不知道这个特性,可以解释一下。 最后,总结两者的优缺点,给出实际应用中的建议,帮助用户根据具体需求选择合适的索引类型。同时,确保回答结构清晰,分点明确,让用户容易理解。</think>### MySQL B+索引与哈希索引对比分析 #### 一、核心区别 1. **数据结构** - **B+索引**:多路平衡搜索,数据存储在叶子节点,形成有序链表[^4] 例如:$$ \text{非叶节点}(K_1,K_2,...,K_n) \rightarrow \text{叶节点}(D_1,D_2,...,D_n) $$ - **哈希索引**:基于哈希表实现,通过哈希函数将键值映射到桶位置 2. **查询模式** - B+支持$范围查询$(如`BETWEEN`、`>`、`<`)和排序操作 - 哈希索引仅支持$等值查询$(`=`、`IN()`),无法处理范围查询[^3] #### 二、性能对比 | 维度 | B+索引 | 哈希索引 | |------------|--------------------------|-------------------------| | 查询时间复杂度 | $O(\log N)$ | 平均$O(1)$,最差$O(N)$ | | 范围查询效率 | 高(顺序访问叶子节点)[^4] | 不支持 | | 排序性能 | 天然有序 | 需要额外排序步骤 | | 索引维护成本 | 插入/删除需要平衡 | 哈希冲突需处理链式结构 | #### 三、存储特性 1. **B+优势** - 支持$覆盖索引$(索引包含查询字段) - 内部节点不存储数据,节省空间[^4] 例如:$$ \text{存储效率}=1-\frac{\text{数据节点数}}{\text{总节点数}} $$ - 支持多列联合索引的最左前缀匹配 2. **哈希局限** - 无法使用索引完成排序 - 不支持部分索引(如`LIKE 'prefix%'`) - 哈希冲突会降低性能 #### 四、典型应用场景 1. **优先选择B+** - 订单系统的日期范围查询 ```sql SELECT * FROM orders WHERE order_date BETWEEN '2023-01-01' AND '2023-06-30' ``` - 需要`ORDER BY`的报表查询 - 联合索引查询(如`(last_name, first_name)`) 2. **适合哈希索引** - 内存表(MEMORY引擎)的高速等值查询 ```sql SELECT session_data FROM user_sessions WHERE session_id = 'a1b2c3' ``` - 数据仓库的精确匹配ETL过程 - InnoDB自适应哈希索引(自动缓存热点数据)[^1] #### 五、选择建议 1. **OLTP系统**:90%以上使用B+索引 2. **OLAP系统**:在明确只有点查询时考虑哈希索引 3. **混合负载**:使用InnoDB自适应哈希索引,自动平衡两者优势
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值