索引按数据结构分类

118 阅读4分钟

InnoDB、MyISAM 和 Memory 分别支持的索引类型 image.png 由图可见,B+Tree 索引类型是 MySQL 存储引擎采用最多的索引类型

在创建表时,InnoDB 存储引擎会根据不同的场景选择不同的列作为索引:

  • 如果有主键,默认会使用 主键 作为聚簇索引的索引键(key)
  • 如果没有主键,就选择第一个 不包含 NULL 值的唯一列作为聚簇索引的索引键(key)
  • 在上面两个都没有的情况下,InnoDB 将自动生成一个 隐式自增 id 列作为聚簇索引的索引键(key)

其它索引都属于辅助索引(Secondary Index)[也称为二级索引或非聚簇索引]

创建的主键索引和二级索引默认使用的是 B+Tree 索引

先创建一张商品表,id 为主键,如下:

CREATE TABLE `product`  (
  `id` int(11) NOT NULL,
  `product_no` varchar(20)  DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL,
  `price` decimal(10, 2) DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

商品表里,有这些行数据: image.png

B+Tree

B+Tree 是一种多叉树,叶子节点才存放数据,非叶子节点只存放索引,而且每个节点里的数据是按主键顺序存放的。每一层父节点的索引值都会出现在下层子节点的索引值中,因此在叶子节点中,包括了所有的索引值信息,并且每一个叶子节点都指向下一个叶子节点,形成一个链表

image.png 主键索引的 B+Tree 图

通过主键查询商品数据的过程

执行查询语句,使用了主键索引查询 id 号为 5 的商品

查询过程:B+Tree 会自顶向下逐层进行查找

  • 将 5 与根节点的索引数据 (1,10,20) 比较,5 在 1 和 10 之间,所以根据 B+Tree的搜索逻辑,找到第二层的索引数据 (1,4,7)
  • 在第二层的索引数据 (1,4,7)中进行查找,因为 5 在 4 和 7 之间,所以找到第三层的索引数据(4,5,6)
  • 在叶子节点的索引数据(4,5,6)中进行查找,然后找到了索引值为 5 的行数据

数据库的索引和数据都是存储在硬盘的,我们可以把读取一个节点当作一次磁盘 I/O 操作。那么上面的整个查询过程一共经历了 3 个节点,也就是进行了 3 次 I/O 操作

千万级的数据在主存中根本存储不下,只能以块的形式从磁盘读取数据,与主存的访问时间相比,磁盘的 I/O 操作相当耗时

B+Tree 存储千万级的数据只需要 3-4 层高度就可以满足,这意味着从千万级的表查询目标数据最多需要 3-4 次磁盘 I/O,所以B+Tree 相比于 B 树和二叉树来说,最大的优势在于查询效率很高,因为即使在数据量很大的情况,查询一个数据的磁盘 I/O 依然维持在 3-4次

通过二级索引查询商品数据的过程

主键索引的 B+Tree 和二级索引的 B+Tree 区别如下:

  • 主键索引的 B+Tree 的叶子节点存放的是实际数据,所有完整的用户记录都存放在主键索引的 B+Tree 的叶子节点里
  • 二级索引的 B+Tree 的叶子节点存放的是主键值,而不是实际数据

将前面的商品表中的 product_no (商品编码)字段设置为二级索引,二级索引的 B+Tree 如下图,其中非叶子的 key 值是 product_no(图中橙色部分),叶子节点存储的数据是主键值(图中绿色部分)

image.png

用 product_no 二级索引查询商品

select * from product where product_no = '0002';

先检二级索引中的 B+Tree 的索引值(商品编码,product_no),找到对应的叶子节点,然后获取主键值,然后再通过主键索引中的 B+Tree 树查询到对应的叶子节点,然后获取整行数据。这个过程叫「回表」,要查两个 B+Tree 才能查到数据

image.png 当查询的数据是能在二级索引的 B+Tree 的叶子节点里查询到,这时就不用再查主键索引查,比如下面这条查询语句(查询主键):

select id from product where product_no = '0002';

这种在二级索引的 B+Tree 就能查询到结果的过程就叫作「覆盖索引」,只需要查一个 B+Tree 就能找到数据