概览

本篇文章将带领你了解Innodb索引的底层数据结构,各种索引之间的区别。

Innodb索引的底层数据结构

上一篇文章我们提到过MYSQL的存储引擎负责数据存储,同时也向执行器提供接口调用。MYSQL的存储引擎是插件式的,用户在创建表的同时可以指定存储引擎。

create table example(id int(10) not null auto_increment, k int not null, name varchar(16), primary key(id), index (k) )ENGINE=INNODB;

上述sql创建了一张名为example的表,同时指定存储引擎是Innodb。Innodb是MYSQL应用最广的存储引擎,具有支持事务和行级锁等特性。在索引的底层设计上,Innodb采用了B+树的数据结构。

为什么要采用B+树作为索引的底层数据结构呢?数据库存储的数据都是存储在磁盘上的,现代机械磁盘的读取速度都在毫秒级别,相较于cpu的计算速度来说相差了很大一截,这就会在并发量大的业务场景下出现很大瓶颈。如何提高数据库的查询效率,也就是减少相对应数据的查找次数。 B+树是一种多叉的平衡查找树,所谓多叉是指每个父节点最多同时具有N个子节点,平衡是指各个子树的高度差的绝对值不超过1。树的高度决定了数据查找时最多的查找次数,在相同数据量下,树的叉树越多,那么树的高度就会越低,最大查找次数也会减少,其时间复杂度为O(log(N)),为了树的平衡性,在每次树的节点更新(增加记录,删除记录)时都会进行一次树的结构的调整,其时间复杂度也为O(log(N))。

聚集索引和非聚集索引之间的区别

MYSQL数据库的索引可以分为聚集索引和非聚集索引。其中聚集索引是指数据的在磁盘上的存放顺序与索引的顺序一致。一般在创建数据库的主键的同时,数据库会为该主键创建一个聚集索引。按照我们上述的观点,索引在数据库是用B+树的结构存储的,B+树的叶子节点就是实际的数据,而非叶子节点只起到索引的作用。而除了聚集索引以外的非聚集索引,其叶子节点存储的则是主键值(如id,联合索引除外!),如果主键是身份证号码的话,那么非聚集索引的每个叶子节点就要占据差不多20个字节。所以主键的大小往往影响了非聚集索引所占用的空间大小。在通过非聚集索引进行查询时,首先得到的是主键值,然后会通过主键值再次回表查询,这时才拿到我们需要的数据。

![聚集索引图](/assets/wp-content/uploads/2019/04/未命名文件-48.png)
聚集索引存储示例图

通过非聚集索引回表查询示例:

select name from example where k=6;

![非聚集索引回表查询图](/assets/wp-content/uploads/2019/04/未命名文件-47-768x482.png)
非聚集索引回表查询示例图

联合索引

联合索引是一种多列索引。
create index t1_username_password_sex on t1(username,password,sex)
上述sql语句在t1表上创建一个包含username,password和sex列的联合索引。联合索引遵循最左前缀的原则,也就是说,在where语句的顺序为(username),(username,password)和(username,password,sex)这三种条件下,联合索引有效,能够提高查询效率。
select * from t1 where username = ‘张三’
select * from t1 where username = ‘张三’ and password = ‘1234’
select * from t1 where username = ‘张三’ and password = ‘1234’ and sex = 0
联合索引是对在前一个条件筛选得到的子集进行再一次条件筛选。比如上述最后一条sql语句。首先通过username进行筛选,拿到所有名字为张三的记录,然后在这个子集中找到所有密码为1234的记录,最后对满足的记录进行性别筛选。
在我们创建联合索引的时候可以考虑哪些点呢?空间节约。现在有个业务场景,既要通过日期(date)查询一天内所有商品的成交量,又要通过日期和商品(item)查询特定商品的日成交量。这时候如果联合索引的顺序为(date,item),那么我们就不用再去维护一个date的单列索引。

![联合索引图](/assets/wp-content/uploads/2019/04/未命名文件-49.png)
联合索引示例图

覆盖索引到底是什么?

在前一节我们提到过,通过非聚集索引进行查询如果无法获取到所有所需信息时,这时我们又要回表通过主键值再一次进行查询,这样很影响性能。那么是否能够只查询非聚集索引就能够获得我们想要的信息呢?覆盖索引!所谓覆盖索引就是指索引包含我们所有需要查询的信息时,那么该索引就能够称之为覆盖索引,这时就不需要回表查询。最简单的一种覆盖索引就是我们只需要获得主键值时。

create table example(id int(10) not null auto_increment, name varchar(20), KEY index1\_name (name), primary key(id))ENGINE=INNODB;

上述这条sql语句有个id主键,同时也有name列上的索引。

select id from example where name = ‘张三’

当我们只需要获得张三的id时,那么这时通过该非聚集索引进行查询就不需要回表。

那么覆盖索引在生产环境有什么指导意义呢?举个例子,如果我们经常需要通过用户的用户名查询他的性别时,那么就可以建立一个用户名和性别的联合索引,此时这个联合索引的B+树里的每个叶子节点都存储了相应用户的用户名、性别以及主键值。