Skip to content

索引

1. 索引概述

MySQL官方对索引的定义为: 索引(index)是帮助MySQL高效获取数据的数据结构。

2. 优缺点

2.1 优点

  1. 提高数据检索的效率,降低数据库的IO成本。
  2. 通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消耗。

2.2 缺点

  1. 索引列也是要占用空间的。
  2. 索引大大提高了查询效率,同时却也降低更新表的速度,如对表进行INSERT、UPDATE、DELETE时,效率降低。

3. 索引结构

3.1 索引类型

MySQL的索引是在存储引擎层实现的,不同的存储引擎有不同的结构,主要包含以下几种:

索引结构描述
B+tree索引最常见的索引类型,大部分引擎都支持B+树索引
hash索引底层数据结构是用哈希表实现的,
只有精确匹配索引列的查询才有效,不支持范围查询
R-tree(空间索引)空间索引是MyISAM引擎的一个特殊索引类型,
主要用于地理空间数据类型,通常使用较少
Full-text(全文索引)是一种通过建立倒排索引,快速匹配文档的方式。类似于Lucene,Solr,ES

3.2 索引支持情况

不同的存储引擎对索引支持情况各不相同:

索引InnoDBMyISAMMemory
B+tree索引支持支持支持
hash索引不支持不支持支持
R-tree索引不支持支持不支持
Full-text索引5.6之后支持支持不支持

我们平常所说的索引,如果没有特别指明,都是指B+树结构组织的索引。每个索引(包括主键索引、二级索引)都会生成一棵独立的B+树。

3.3 B+树结构

选择二叉查找树:
Alt text 二叉查找树的缺点:顺序插入时,会形成一个链表,查询性能大大降低。大数据量情况下,层级较深,检索速度慢。
选择红黑树:
Alt text 红黑树利用红黑规则可以规避第一个问题,避免形成链表,但是大数据量情况下,仍然存在层级较深,检索速度慢。
在MySQL中使用独有的B+tree(多路平衡查找树) Alt text 每一个数据页(mysql默认16k)包含指针、数据, 如上图所示主键索引,最大度数为4的B+树,每个节点比如最多存储3个key,4个指针(指针永远比key多一个)

3.4 MySQL的B+树

MySQL索引数据结构对经典的B+Tree进行了优化。在原B+Tree的基础上,增加一个指向相邻叶子节点的链表指针,就形成了带有顺序指针的B+Tree,提高区间访问的性能。 Alt text

4. hash索引

4.1 结构

哈希索引就是采用一定的hash算法,将键值换算成新的hash值,映射到对应的槽位上,然后存储在hash表中。 Alt text 如果两个(或多个)键值,映射到一个相同的槽位上,他们就产生了hash冲突(也称为hash碰撞),可以通过链表来解决。
Alt text

4.2 特点

  1. Hash索引只能用于对等比较(=,in),不支持范围查询(between,>,< ,...)
  2. 无法利用索引完成排序操作(因为hash值计算结果并不连续)
  3. 查询效率高,通常(不存在hash冲突的情况)只需要一次检索就可以了,效率通常要高于B+tree索引

4.3 存储引擎支持

在MySQL中,支持hash索引的是Memory存储引擎。 而InnoDB中具有自适应hash功能,hash索引是InnoDB存储引擎根据B+Tree索引在指定条件下自动构建的。

为什么InnoDB存储引擎选择使用B+tree索引结构?

  1. 相对于二叉树,层级更少,搜索效率高;
  2. 对于B树,无论是叶子节点还是非叶子节点,都会保存数据,这样导致一页中存储的键值减少,指针跟着减少,要同样保存大量数据,只能增加树的高度,导致性能降低;同时叶子节点查找是单向链表没有双向链表灵活。
  3. 相对Hash索引,B+tree支持范围匹配及排序操作;

5. 索引组织表IOT

在MySQL中,InnoDB存储引擎默认使用IOT结构,即表数据按主键顺序聚集索引组织。对于聚集索引,叶子节点就是存储数据;对于二级索引叶子节点存储索引列和主键。至于非叶子节点,存储的是索引列和指向叶子的指针(Innodb默认是6个字节)。

sql
-- 无需额外语法,创建表时定义主键即可自动形成 IOT
CREATE TABLE t_emp (
  -- 主键索引作为聚集索引,数据按emp_id排序
  emp_id INT PRIMARY KEY,  
  name VARCHAR(100),
  salary DECIMAL(10,2)
);

如果要计算B+树能够支持多少条数据,比如主键是bigint类型占用8个字节,每行数据平均占用300个字节,页大小是16K:

层级总共数据量
116k/300≈50条,只有root page
2root page不再存数据:16K/(8+6)≈1000,
叶子节点为50*1000=5W条
3root page不再存数据:16K/(8+6)≈1000,
二级节点:1000*1000=100W,
叶子节点为: 100W*50=5000W条
4root page不再存数据:16K/(8+6)≈1000,
二级节点:1000*1000=100W,
三级节点:1000*1000*1000=10亿
叶子节点为: 10亿*50=500亿条

可以看出普通的数据量一般B+树的层级一般最多就达到4层,数据查询的IO次数(也就是访问数据页文件)的差别不超过3,IO性能都是差不多的。比如普通的HDD硬盘一秒能做100次IO读,在B+树中找3次(也就是层级为3),消耗的时间为0.03s, 找4次消耗的时间为0.04s,如果是SSD盘差距更小,而且MySQL一般会把root page放到内存中。当然不可否认单表查询数据很稳定很快,但是如果多表关联查询仍然会随着数据变多而显著的变慢。

sql
-- 索引页中可用的占比,默认使用全部,
-- 插入新数据时,若页已满会触发 页分裂(Page Split)
show variables like '%innodb_fill_factor%';

6. 堆表

堆表(Heap Table)是一种基于 “堆” 数据结构存储数据的表类型,其数据行的存储顺序与插入顺序无关,也不依赖索引排序。所有的索引都是非聚集索引/二级索引,叶子page存储的是数据地址,每一页大小为1K。常见于MyISAM、Memeory引擎中。Oracle和PG数据库都是堆表的方式组织索引。 Alt text 堆表由于叶子节点直接存储了数据地址,所以不用像IOT表有回表的现象。缺点就是堆表里面存放的数据没有顺序,导致范围查找性能没有IOT表高。

sql
-- 表示使用页大小的比例是多少
show variables like '%innodb_fill_factor%';