Skip to content

Commit 4c22192

Browse files
committed
MySQL 哈希索引索引
1 parent a1147b4 commit 4c22192

3 files changed

Lines changed: 56 additions & 5 deletions

File tree

MySQL/MySQL必知必会.md

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -229,11 +229,11 @@ MySQL 官方对索引的定义为:索引(index)是帮助 MySQL 搞笑获
229229

230230
**首先要明白索引(index)是在存储引擎(storage engine)层面实现的,而不是 server 层面。** 不是所有的存储引擎都支持所有的索引类型。即使多个存储引擎支持某一索引类型,它们的实现和行为也可能有所差别。
231231

232-
### B+ Tree 索引
232+
### B+Tree 索引
233233

234234
MyISAM 和 InnoDB 存储引擎,都使用 B+ Tree 的数据结构,它相对与 B-Tree 结构,所有的数据都存放在叶子节点上,且把叶子节点通过指针连接到一起,形成了一条数据链表,以加快相邻数据的检索效率。
235235

236-
### B-Tree
236+
#### B-Tree
237237

238238
B-Tree 是为磁盘等外存储设备设计的一种平衡查找树。系统从磁盘读取数据到内存时是以磁盘块(block)为基本单位的,位于同一个磁盘块中的数据会被一次性读取出来,而不是需要什么取什么。InnoDB 存储引擎中有页(Page)的概念,页是其磁盘管理的最小单位。InnoDB 存储引擎中默认每个页的大小为 16KB,可通过参数`innodb_ page_ size`将页的大小设置为 4K、8K、16K,在 MySQL 中可通过如下命令查看页的大小:`show variables like 'innodb_ page_ size'`
239239

@@ -269,7 +269,7 @@ B-Tree 中的每个节点根据实际情况可以包含大量的关键字信息
269269

270270
分析上面过程,发现需要 3 次磁盘 I/O 操作,和 3 次内存查找操作。由于内存中的关键字是一个有序表结构,可以利用二分法查找提高效率。而 3 次磁盘 I/O 操作是影响整个 B-Tree 查找效率的决定因素。B-Tree 相对于 AVLTree 缩减了节点个数,使每次磁盘 I/O 取到内存的数据都发挥了作用,从而提高了查询效率。
271271

272-
### B+Tree
272+
#### B+Tree
273273

274274
B+Tree 是在 B-Tree 基础上的一种优化,使其更适合实现外存储索引结构,InnoDB 存储引擎就是用 B+Tree 实现其索引结构。从上一节中的 B-Tree 结构图中可以看到每个节点中不仅包含数据的 key 值,还有 data 值。而每一个页的存储空间是有限的,如果 data 数据较大时将会导致每个节点(即一个页)能存储的 key 的数量很小,当存储的数据量很大时同样会导致 B-Tree 的深度较大,增大查询时的磁盘 I/O 次数,进而影响查询效率。 **在 B+Tree 中,所有数据记录节点都是按照键值大小顺序存放在同一层的叶子节点上,而非叶子节点上只存储 key 值信息,这样可以大大加大每个节点存储的 key 值数量,降低 B+Tree 的高度。**
275275

@@ -291,12 +291,12 @@ InnoDB 存储引擎中页的大小为 16KB,一般表的主键类型为 INT(
291291

292292
实际情况中每个节点可能不能填充满,因此在数据库中,B+Tree 的高度一般都在 2-4 层。MySQL 的 InnoDB 存储引擎在设计时是将根节点常驻内存的,也就是说查找某一键值的行记录时最多只需要 1~3 次磁盘 I/O 操作。
293293

294-
### B+Tree 性质
294+
#### B+Tree 性质
295295

296296
1. 通过上面的分析,我们知道 IO 次数取决于 B+Tree 的高度 h,假设当前数据表的数据为 N,每个磁盘块的数据项的数量是 m,则有 `h=log(m+1)N`,当数据量 N 一定的情况下,m 越大,h 越小;而 m = 磁盘块的大小 / 数据项的大小,磁盘块的大小也就是一个数据页的大小,是固定的,如果数据项占的空间越小,数据项的数量越多,树的高度越低。这就是为什么每个数据项,即索引字段要尽量的小,比如 int 占 4 字节,要比 bigint 8 字节少一半。这也是为什么 B+Tree 要求把真实的数据放到叶子节点而不是内层节点,一旦放到内层节点,磁盘块的数据项会大幅度下降,导致树增高。当数据项等于 1 时将会退化成线性表。
297297
2. 当 B+Tree 的数据项是复合的数据结构,比如(name,age,sex)的时候,B+Tree 是按照从左到右的顺序来建立搜索树的,比如当(张三,20,F)这样的数据来检索的时候,B+Tree 会优先比较 name 来确定下一步的所搜方向,如果 name 相同再依次比较 age 和 sex,最后得到检索的数据;但当(20,F)这样的没有 name 的数据来的时候,B+Tree 就不知道下一步该查哪个节点,因为建立搜索树的时候 name 就是第一个比较因子,必须要先根据 name 来搜索才能知道下一步去哪里查询。比如当(张三,F)这样的数据来检索时,B+Tree 可以用 name 来指定搜索方向,但下一个字段 age 的缺失,所以只能把名字等于张三的数据都找到,然后再匹配性别是 F 的数据了,这个是非常重要的性质,即 **索引的最左匹配特性。**
298298

299-
### MyISAM 主键索引与辅助索引的结构
299+
#### MyISAM 主键索引与辅助索引的结构
300300

301301
MyISAM 引擎的索引文件和数据文件是分离的。 **MyISAM 引擎索引结构的叶子节点的数据域,存放的并不是实际的数据记录,而是数据记录的地址。** 索引文件与数据文件分离,这样的索引称为"非聚簇索引"。MyISAM 的主索引与辅助索引区别并不大,只是主键索引不能有重复的关键字。
302302

@@ -307,3 +307,54 @@ MyISAM 引擎的索引文件和数据文件是分离的。 **MyISAM 引擎索引
307307
主索引是指主键索引,键值不可能重复;辅助索引则是普通索引,键值可能重复。
308308

309309
通过索引查找数据的流程:先从索引文件中查找到索引节点,从中拿到数据的文件指针,再到数据文件中通过文件指针定位了具体的数据。辅助索引类似。
310+
311+
##### InnoDB 主键索引与辅助索引的结构
312+
313+
InnoDB 引擎索引结构的叶子节点的数据域,存放的就是实际的数据记录(对于主索引,此处会存放表中所有的数据记录;对于辅助索引此处会引用主键,检索的时候通过主键到主键索引中找到对应数据行),或者说,InnoDB 的数据文件本身就是主键索引文件,这样的索引被称为“聚簇索引”,一个表只能有一个聚簇索引。
314+
315+
**主键索引:**
316+
317+
我们知道 InnoDB 索引是聚集索引,它的索引和数据是存入同一个`.idb`文件中的,因此它的索引结构是在同一个树节点中同时存放索引和数据,如下图中最底层的叶子节点有三行数据,对应于数据表中的 id、stu_ id、name 数据项。
318+
319+
![image-20210524230642750](images/image-20210524230642750.png)
320+
321+
在 InnoDB 中,索引分叶子节点和非叶子节点,非叶子节点就像新华字典的目录,单独存放在索引段中,叶子节点则是顺序排列的,在数据段中,InnoDB 的数据文件可以按照表来切分(只需要开启 `innodb_ file_ per_ table`),切分后存放在 `xxx.ibd` 中,默认不切分,存放在`xxx.ibdata`中。
322+
323+
**辅助(非主键)索引:**
324+
325+
这次我们以示例中学生表中的 name 列建立辅助索引,它的索引结构跟主键索引的结构有很大差别,在最底层的叶子结点有两行数据,第一行的字符串是辅助索引,按照 ASCII 码进行排序,第二行的整数是主键的值。这就意味着,对 name 列进行条件搜索,需要两个步骤:
326+
327+
1. 在辅助索引上检索 name,到达其叶子节点获取对应的主键;
328+
2. 使用主键在主索引上再进行对应的检索操作
329+
330+
这也就是所谓的“回表查询”
331+
332+
![image-20210524231050881](images/image-20210524231050881.png)
333+
334+
##### InnoDB 索引结构需要注意的点
335+
336+
1. 数据文件本身就是索引文件
337+
2. 表数据文件本身就是按 B+Tree 组织的一个索引结构文件
338+
3. 聚集索引中叶节点包含了完整的数据记录
339+
4. InnoDB 表必须要有主键,并且推荐使用整型自增主键
340+
341+
正如上面介绍 InnoDB 存储结构,索引与数据是共同存储的,不管是主键索引还是辅助索引,在查找时都是通过先查找到索引节点才能拿到相对应的数据,如果在设计表结构时没有显式指定索引列的话,MySQL 会从表中选择数据不重复的列建立索引,如果没有符合的列,则 MySQL 自动为 InnoDB 表生成一个隐含字段作为主键,并且这个字段长度为 6 个字节,类型为整型。
342+
343+
> 为什么推荐使用整型自增主键而不是选择 UUID?
344+
>
345+
> - UUID 是字符串,比整型消耗更多的存储空间;
346+
> - 在 B+Tree 中进行查找时需要跟经过的节点值比较大小,整型数据的比较运算比字符串更快速;
347+
> - 自增的整型索引在磁盘中会连续存储,在读取一页数据时也是连续;UUID 是随机产生的,读取的上下两行数据存储是分散的,不适合执行`where id > 5 && id <20`的条件查询语句;
348+
> - 在插入或删除数据时,整型自增主键会在叶子结点的末尾建立新的叶子节点,不会破坏左侧子树的结构;UUID 主键很容易出现这样的情况,B+Tree 为了维持自身的特性,有可能会进行结构的重构,消耗更多的时间。
349+
350+
> 为什么非主键索引结构叶子节点存储的是主键值?
351+
>
352+
> 保证数据一致性和节省存储空间,可以这么理解:商城系统订单表会存储一个用户 ID 作为关联外键,而不推荐存储完整的用户信息,因为当我们用户表中的信息(真实名称、手机号、收货地址..)修改后,不需要再次维护订单表的用户数据,同时也节省了存储空间。
353+
354+
### Hash 索引
355+
356+
主要就是通过 Hash 算法(常见的 Hash 算法有直接定址法、平方取中法、折叠法、除数取余法、随机数法),将数据库字段数据转换成定长的 Hash 值,与这条数据的行指针一并存入 Hash 表的对应位置;如果发生 Hash 碰撞(两个不同关键字的 Hash 值相同),则在对应 Hash 键下以链表形式存储。
357+
358+
检索算法:在检索查询时,就再次对待查关键字再次执行相同的 Hash 算法,得到 Hash 值,到对应 Hash 表对应位置取出数据即可,如果发生 Hash 碰撞,则需要在取值时进行筛选。目前使用 Hash 索引的数据库并不多,主要有 Memory 等。
359+
360+
MySQL 目前有 Memory 引擎和 NDB 引擎支持 Hash 索引。
736 KB
Loading
578 KB
Loading

0 commit comments

Comments
 (0)