MySQL深度剖析-索引原理由浅入深

时间:2024-11-19 15:28:09

什么是索引?

官方上面说索引是帮助MySQL高效获取数据的数据结构,通俗点的说,数据库索引好比是一本书的目录,可以直接根据页码找到对应的内容,目的就是为了加快数据库的查询速度。

索引是对数据库表中一列或多列的值进行排序的一种结构,使用索引可快速访问数据库表中的特定信息。

如果没有加索引,我们会对数据库中的数据逐行比对,效率极低,所以我们可以根据索引快速定位到我们需要查询的数据,拿到定位后的信息再去查找。

索引的原理

那么问题来了,索引肯定要高效,如果只是用一个普通数组存下来,那不依然是遍历一遍效率很低吗,所以索引一定是一个非常高效的数据结构

索引的存储原理可以概括为一句话:以空间换时间

一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往是存储在磁盘上的文件中的(可能存储在单独的索引文件中,也可能和数据一起存储在数据文件中)。

数据库在未添加索引进行查询的时候默认是进行全文搜索,也就是说有多少数据就进行多少次查询,然后找到相应的数据就把它们放到结果集中,直到全文扫描完毕。

索引有哪些?

主键索引

  • 设定为主键后,数据库自动建立索引,InnoDB为聚簇索引,主键索引列值不能为空(Null)
# (1) 创建表添加主键索引
CREATE TABLE `table_name` (
    [...] ,
    PRIMARY KEY (`col_name`),
)
# (2) 添加主键索引
ALTER TABLE `table_name` ADD PRIMARY KEY (`col_name`);

普通索引(单列索引)

  • 普通索引(单列索引):单列索引是最基本的索引,它没有任何限制。
#  (1) 直接创建索引
CREATE INDEX index_name ON table_name(`col_name`);
#  (2) 修改表结构的方式添加索引
ALTER TABLE `table_name` ADD INDEX index_name(`col_name`);
#  (3) 创建表的时候同时创建索引
CREATE TABLE `table_name` (
    [...] ,
    PRIMARY KEY (`id`),
    INDEX index_name (`col_name`)
)
#  (4) 删除索引
DROP INDEX index_name ON table_name;   
alter table `表名` drop index 索引名;

复合索引(组合索引)

  • 复合索引:复合索引是在多个字段上创建的索引。复合索引遵守“最左前缀”原则即在查询条件中使用了复合索引的第一个字段,索引才会被使用。因此,在复合索引中索引列的顺序至关重要。
# (1)创建一个复合索引
create index index_name on table_name(`col_name1`,`col_name2`,...);
# (2)修改表结构的方式添加索引
alter table table_name add index index_name(`col_name1`,`col_name2`,...);

唯一索引

唯一索引:唯一索引和普通索引类似,主要的区别在于,唯一索引限制列的值必须唯一,但允许存在空值(只允许存在一条空值)

如果在已经有数据的表上添加唯一性索引的话:

  • 如果添加索引的列的值存在两个或者两个以上的空值,则不能创建唯一性索引会失败。(一般在创建表的时候,要对自动设置唯一性索引,需要在字段上加上 not null)
  • 如果添加索引的列的值存在两个或者两个以上的null值,还是可以创建唯一性索引,只是后面创建的数据不能再插入null值 ,并且严格意义上此列并不是唯一的,因为存在多个null值。
  • 对于多个字段创建唯一索引规定列值的组合必须唯一
-- (1)创建唯一索引
# 创建单个索引
CREATE UNIQUE INDEX index_name ON table_name(`col_name`);

# 创建多个索引
CREATE UNIQUE INDEX index_name on table_name(`col_name`,...);

-- (2)修改表结构
# 单个
ALTER TABLE table_name ADD UNIQUE index index_name(`col_name`);
# 多个
ALTER TABLE table_name ADD UNIQUE index index_name(`col_name`,...);

-- (3)创建表的时候直接指定索引
CREATE TABLE `table_name` (
    [...] ,
    PRIMARY KEY (`id`),
    UNIQUE index_name_unique(`col_name`)
)

“空值” 和”NULL”的概念: 
1:空值是不占用空间的 .
2: MySQL中的NULL其实是占用空间的.

长度验证:注意空值的之间是没有空格的。

> select length(''),length(null),length(' ');
+------------+--------------+-------------+
| length('') | length(null) | length(' ') |
+------------+--------------+-------------+
|          0 |         NULL |           1 |
+------------+--------------+-------------+

全文索引

  • Full Text类型索引(FULLTEXT 索引在 MySQL 5.6 版本之后支持 InnoDB,而之前的版本只支持 MyISAM 表)。
  • 全文索引主要用来查找文本中的关键字,而不是直接与索引中的值相比较,目前只有char、varchar,text 列上可以创建全文索引。
-- (1)创建表的适合添加全文索引
CREATE TABLE `table_name` (
    [...] ,
    PRIMARY KEY (`id`),
    FULLTEXT (`col_name`)
)

-- (2)修改表结构添加全文索引
ALTER TABLE table_name ADD FULLTEXT index_fulltext_content(`col_name`)

-- (3)直接创建索引
CREATE FULLTEXT INDEX index_fulltext_content ON table_name(`col_name`)

注意:

  • 默认 MySQL 不支持中文全文检索!
  • MySQL 全文搜索只是一个临时方案,对于全文搜索场景,更专业的做法是使用全文搜索引擎,例如 ElasticSearch 或 Solr。

索引的查询和删除命令:

#查看:
show indexes from `表名`;
#或
show keys from `表名`;
 
#删除
alter table `表名` drop index 索引名;

索引的优缺点

优点:

  • 大大提高数据查询速度。
  • 可以提高数据检索的效率,降低数据库的IO成本,类似于书的目录。
  • 通过索引列对数据进行排序,降低数据的排序成本降低了CPU的消耗。
  • 被索引的列会自动进行排序,包括【单例索引】和【组合索引】,只是组合索引的排序需要复杂一些。
  • 如果按照索引列的顺序进行排序,对order 不用语句来说,效率就会提高很多。

缺点:

  • 索引会占据磁盘空间。
  • 索引虽然会提高查询效率,但是会降低更新表的效率。比如每次对表进行增删改查操作,MySQL不仅要保存数据,还有保存或者更新对应的索引文件。
  • 维护索引需要消耗数据库资源。

所以,综合索引的优缺点:

数据库表中不是索引越多越好,而是仅为那些常用的搜索字段建立索引效果最佳!(也即where后面常用到的字段建立索引效果最佳)

索引的数据结构

前面我们说到,索引就类似于目录,索引也是一个数据结构,存放一些字段对应的磁盘地址,索引的特点就是要高效快速定位查找等,不然就简简单单用一个数组查询,还是一样的全表扫描遍历。

所以,索引一定是用高效查找的数据结构来实现的!!!

我们来依次看看有哪些数据结构用来做索引,以及各种数据结构的优缺点和为什么InnoDB默认采用的B+树(对应的面试题:为什么MySQL存储引擎InnoDB索引的底层数据结构采用B+树

Hash

Hash表,在Java中的HashMap,TreeMap就是Hash表结构,以键值对的形式存储数据。我们使用hash表存储表数据结构,Key可以存储索引列,Value可以存储行记录或者行磁盘地址。Hash表在等值查询时效率很高,时间复杂度为O(1);但是不支持范围快速查找(因为Hash表是无序的呀!),范围查找时只能通过扫描全表的方式,筛选出符合条件的数据。

显然这种方式,不适合我们经常需要查找和范围查找的数据库索引使用。

二叉树

上面这个图就是我们常说的二叉树:每个节点最多有两个分叉节点,左子树和右子树数据按顺序左小右大。

二叉树的特点就是为了保证每次查找都可以进行折半查找,从而减少IO次数。

但是二叉树不是一直保持二叉平衡,因为二叉树很考验根节点的取值,因为很容易在某个节点下不分叉了,这样的话二叉树就不平衡了,也就没有了所谓的能进行折半查找了,如下图:

 

对于MySQL每次从磁盘加载的大小为16kb,如果刚好一个节点就是16kb,那么这里查找5号节点就要做5次磁盘IO

显然这种不稳定的情况,我们在选择存储数据结构的时候就会尽量避免这种的情况发生。

另外,二叉树也不适合范围查询,进行范围查询每次都只能从根节点往后找(只找一个)

平衡二叉树(AVL树)

为了解决二叉树在极端情况下会退化成链表的问题,考虑使用平衡二叉树,其特点就是每个节点的左子树和右子树高度差不能超过1,所以他会通过旋转来维护二叉树的平衡性,所以就不会像普通二叉树一样出现“歪脖子”问题

平衡二叉树采用的是二分法思维,平衡二叉查找树除了具备二叉树的特点,最主要的特征是树的左右两个子树的层级最多差1。在插入删除数据时通过左旋/右旋操作保持二叉树的平衡,不会出现左子树很高、右子树很矮的情况。

使用平衡二叉查找树查询的性能接近与二分查找,时间复杂度为O(log2n),查询id=6,只需要两次IO。

 

就上述平衡二叉树的特点来看,其实是我们理想的状态下,然而其实内部还是存在一些问题:

  • 时间复杂度和树的高度有关。树有多高就需要检索多少次,每个节点的读取,都对应一次磁盘的IO操作。树的高度就等于每次查询数据时磁盘IO操作的次数。磁盘每次寻道的时间为10ms,在数据量大时,查询性能会很差。(1百万的数据量,log2n约等于20次磁盘IO读写,时间消耗约等于:20*10=0.2S)。
  • 平衡二叉树不支持范围查询快速查找,范围查询需要从根节点多次遍历,查询效率不高。

B树

AVL树、红黑树都是常见的平衡二叉树,不管是哪个,其一个节点最多只能有两个分支,都会随着插入的元素增多,而导致树的高度变高,就意味着磁盘IO的次数变多,会影响整体数据的查询效率

那么就有一个需求了:在树的高度不那么高的情况下,如何能满足存储大数据量

答:多叉平衡树

MySQL的数据是存储在磁盘文件中的,查询处理数据时,需要先把磁盘中的数据加载到内存中,磁盘IO操作非常耗时,所以我们优化的重点就是尽量减少磁盘的IO操作。访问二叉树的每个节点都会发生一次IO,如果想要减少磁盘IO操作,就需要尽量降低树的高度。

那如何降低树的高度呢?

假如key为bigint=8字节,每个节点有两个指针,每个指针为4个字节,一个节点占用的空间为(8+4*2=16)。

因为在MySQL的InnoDB引擎的一次IO操作会读取一页的数据量(默认一页大小为16K),而二叉树一次IO操作的有效数据量只有16字节,空间利用率极低。为了最大化的利用一次IO操作空间,一个解决方法就是在一个节点处存储多个元素,在每个节点尽可能多的存储数据。每个节点可以存储1000个索引(16k/16=1000),这样就将二叉树改造成了多叉树,通过增加树的分叉树,将树的体型从高瘦变成了矮胖。构建1百万条数据,树的高度需要2层就可以(1000*1000=1百万),也就是说只需要两次磁盘IO操作就可以查询到数据,磁盘IO操作次数变少了,查询数据的效率整体也就提高了。

这种数据结构我们称之为B树,B树是一种多叉平衡查找树,如下图主要特点:

  • B树的节点中存储这多个元素,每个内节点有多个分叉。
  • 节点中的元素包含键值和数据,节点中的键值从小到大排列。也就是说,在所有的节点中都存储数据。
  • 父节点当中的元素不会出现在子节点中。
  • 所有的叶子节点都位于同一层,叶子节点具有相同的深度,叶子节点之间没有指针连接。

 举个简单的例子,在B树中查询数据的情况:

假如我们要查询key等于10对应的数据data,根据上图我们可知在磁盘中的查询路径是:
        磁盘块1->磁盘块2->磁盘块6
  • 第一次磁盘IO:将磁盘块1加载到内存中,在内存中从头遍历比较,10<15,走左子树,到磁盘中寻址到磁盘块2。
  • 第二次磁盘IO:将磁盘块2加载到内存中,在内存中从头遍历比较,10>7,走右子树,到磁盘中寻址到磁盘块6。
  • 第三次磁盘IO:将磁盘块6加载到内存中,在内存中从头遍历比较,10=10,找到key=10的位置,取出对应的数据data,如果data存储的是行记录,直接取出数据,查询结束;如果data存储的是行磁盘地址,还需要根据磁盘地址到对应的磁盘中取出数据,查询结束。
相比较二叉平衡查找树,在整个查找过程中,
虽然数据的比较次数并没有明显减少,但是对于磁盘IO的次数会大大减少,
同时,由于我们是在内存中进行的数据比较,所以比较数据所消耗的时间可以忽略不计。
B树的高度一般2至3层就能满足大部分的应用场景,所以使用B树构建索引可以很好的提升查询的效率。

看到上面的情况,觉得B树已经很理想了,但是其中还是存在可以优化的地方:

  • B树不支持范围查询的快速查找,例如:仍然根据上图,我们想要查询10到35之间的数据,查找到10之后,需要回到根节点重新遍历查找,需要从根节点进行多次遍历,查询效率有待提高。
  • 如果data存储的是行记录,行的大小随着列数的增加,所占空间会变大,这时一页中可存储的数据量就会减少,树相应就会变高,磁盘IO次数就会随之增加,有待优化。

B+树

B+树,作为B树的升级版,MySQL在B树的基础上继续进行改造,使用B+树构建索引。B+树和B树最主要的区别在于非叶子节点是否存储数据的问题。

  • B树:叶子节点和非叶子节点都会存储数据。
  • B+树:只有叶子节点才会存储数据,非叶子节点只存储键值key;叶子节点之间使用双向指针连接,最底层的叶子节点形成了一个双向有序链表。

B+树的最底层叶子节点包含了所有的索引项。从图上可以看到,B+树在查找数据的时候,由于数据都存放在最底层的叶子节点上,所以每次查找都需要检索到叶子节点才能查询到数据。所以在需要查询数据的情况下每次的磁盘的IO跟树高有直接的关系,但是从另一方面来说,由于数据都被放到了叶子节点,所以放索引的磁盘块锁存放的索引数量是会跟这增加的,所以相对于B树来说,B+树的树高理论上情况下是比B树要矮的。也存在索引覆盖查询的情况,在索引中数据满足了当前查询语句所需要的全部数据,此时只需要找到索引即可立刻返回,不需要检索到最底层的叶子节点。

举例:等值查询

假如我们查询值等于9的数据。查询路径磁盘块1->磁盘块2->磁盘块6。

  • 第一次磁盘IO:将磁盘块1加载到内存中,在内存中从头遍历比较,9<15,走左路,到磁盘寻址磁盘块2。
  • 第二次磁盘IO:将磁盘块2加载到内存中,在内存中从头遍历比较,7<9<12,到磁盘中寻址定位到磁盘块6。
  • 第三次磁盘IO:将磁盘块6加载到内存中,在内存中从头遍历比较,在第三个索引中找到9,取出data,如果data存储的行记录,取出data,查询结束。如果存储的是磁盘地址,还需要根据磁盘地址到磁盘中取出数据,查询终止。(这里需要区分的是在InnoDB中Data存储的为行数据,而MyIsam中存储的是磁盘地址。)

举例:范围查询

假如我们想要查找9和26之间的数据,查找路径为:磁盘块1->磁盘块2->磁盘块6->磁盘块7

  • 前三次磁盘IO:首先查找到键值为9对应的数据(定位到磁盘块6),然后缓存大结果集中。这一步和前面等值查询流程一样,发生了三次磁盘IO。
  • 继续查询,查找到节点15之后,底层的所有叶子节点是一个有序列表,我们从磁盘块6中的键值9开始向后遍历筛选出所有符合条件的数据。
  • 第四次磁盘IO:根据磁盘块6的后继指针到磁盘中寻址定位到磁盘块7,将磁盘块7加载到内存中,在内存中从头遍历比较,9<25<26,9<26<=26,将数据data缓存到结果集中。
  • 逐渐具备唯一性(后面不会再有<=26的数据),不需要再向后查找,查询结束,将结果集返回给用户。

  可以看到B+树可以保证等值和范围查询的快速查找,MySQL的索引就采用了B+树的数据结构。

MySQL是如何实现索引的

介绍完了索引数据结构,那肯定是要带入到Mysql里面看看真实的使用场景的,所以这里分析Mysql的两种存储引擎的索引实现:MyISAM索引InnoDB索引

InnoDB索引

以下介绍其实在“什么是索引?#索引有哪些?”,下面的介绍会做一些补充

聚簇索引(主键索引)

每个InnoDB表都有一个聚簇索引 ,聚簇索引使用B+树构建,叶子节点存储的数据是整行记录。一般情况下,聚簇索引等同于主键索引,当一个表没有创建主键索引时,InnoDB会自动创建一个ROWID字段来构建聚簇索引。

InnoDB创建索引的具体规则如下:

  • 在表上定义主键PRIMARY KEY,InnoDB将主键索引用作聚簇索引。
  • 如果表没有定义主键,InnoDB会选择第一个不为NULL的唯一索引列用作聚簇索引。
  • 如果以上两个都没有,InnoDB 会使用一个6 字节长整型的隐式字段 ROWID字段构建聚簇索引。该ROWID字段会在插入新行时自动递增。

除聚簇索引之外的所有索引都称为辅助索引。在中InnoDB,辅助索引中的叶子节点存储的数据是该行的主键值。 在检索时,InnoDB使用此主键值在聚簇索引中搜索行记录。

这里以user_innodb为例,user_innodb的id列为主键,age列为普通索引。

CREATE TABLE `user_innodb`
(
  `id`       int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(20) DEFAULT NULL,
  `age`      int(11)     DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  KEY `idx_age` (`age`) USING BTREE
) ENGINE = InnoDB;

  • InnoDB的数据和索引存储在t_user_innodb.ibd文件中,InnoDB的数据组织方式,是聚簇索引。
  • 主键索引的叶子节点会存储数据行,辅助索引的叶子节点只会存储主键值。

 

 等值查询数据:

select * from user_innodb where id = 28;
  1. 先在主键树中从根节点开始检索,将根节点加载到内存,比较28<75,走左路。(1次磁盘IO)
  2. 将左子树节点加载到内存中,比较16<28<47,向下检索。(1次磁盘IO)
  3. 检索到叶节点,将节点加载到内存中遍历,比较16<28,18<28,28=28。查找到值等于28的索引项,直接可以获取整行数据。将改记录返回给客户端。(1次磁盘IO)

磁盘IO数量:3次。

辅助索引(普通索引)

除聚簇索引之外的所有索引都称为辅助索引,InnoDB的辅助索引只会存储主键值而非磁盘地址也非行记录。

以表user_innodb的age列为例,age索引的索引结果如下图。

  • 辅助索引的底层叶子节点是按照(age,id)的顺序排序,先按照age列从小到大排序,age相同时按照id列从小到大排序。
  • 使用辅助索引需要检索两遍索引:首先检索辅助索引获得主键,然后根据主键到主键索引中检索获得数据记录。

辅助索引等值查询的情况:

select * from t_user_innodb where age=19;

根据在辅助索引树中获取的主键id,到主键索引树检索数据的过程称为回表查询。

磁盘IO数:辅助索引3次+获取记录回表3次

请记住,这里产生了回表,显然,回表是消耗很多性能的,使查询效率大大降低(磁盘IO次数显而易见的增多),所以我们有覆盖索引来优化他

联合索引

刚才介绍的辅助索引其实就是对单一字段做一个普通索引罢了,其实联合索引就是创建普通索引的时候,多索引几个字段

# (1)创建一个复合索引
create index index_name on table_name(`col_name1`,`col_name2`,...);
# (2)修改表结构的方式添加索引
alter table table_name add index index_name(`col_name1`,`col_name2`,...);

最左匹配原则

它指出,查询条件必须从联合索引的最左边的列开始匹配,且必须连续匹配。只有当查询条件包含联合索引的最左边的列时,数据库引擎才会使用该索引。

使用联合索引时,存在‘最左匹配原则’,要求where后面的查询条件,需要包含构建联合索引的第一个字段

最左前缀匹配原则和联合索引的索引存储结构和检索方式是有关系的。

上面的col_name1/col_name2/col_name3,我在下面就称为a/b/c列

在组合索引树中,最底层的叶子节点按照第一列a列从左到右递增排序,但是b列和c列是无序的,b列只有在a列值相等的情况下小范围内有序递增;而c列只能在a和b两列值相等的情况下小范围内有序递增。

就像上面的查询,B+ 树会先比较a列来确定下一步应该检索的方向,往左还是往右。如果a列相同再比较b列,但是如果查询条件中没有a列,B+树就不知道第一步应该从那个节点开始查起。

可以说创建的idx_(a,b,c)索引,相当于创建了(a)、(a,b)、(a,b,c)三个索引。

接下来通过几条sql来看看最左匹配原则的匹配过程,以及不遵循最左匹配原则导致的索引失效问题

CREATE TABLE `abc_innodb`
(
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `a`  int(11)     DEFAULT NULL,
  `b`  int(11)     DEFAULT NULL,
  `c`  varchar(10) DEFAULT NULL,
  `d`  varchar(10) DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  KEY `idx_abc` (`a`, `b`, `c`)
) ENGINE = InnoDB;

show index from abc_innodb

-- 是否会匹配索引
-- (1)
explain select * from abc_innodb where  a = 1 and b = 2
explain select * from abc_innodb where  b = 2 and a = 1

--  (2)
explain select * from abc_innodb where  b = 2

--  (3)
explain select * from abc_innodb where a = 1 and b>2 and c=3

哪几条SQL会使用索引?(索引的匹配过程)

explain select * from abc_innodb where  a = 1 and b = 2
这一条肯定是匹配的,完全满足最左匹配原则,先按照a来查询索引,a相同的时候,再按照b来找
explain select * from abc_innodb where  b = 2 and a = 1
这一条也是匹配的,但是这里是先按照b来找啊,应该不满足最左匹配原则了啊?
因为这里MySQL的优化器对我们的sql进行了优化:即只要where后面包含了联合索引的第一个字段,我就能匹配到索引
explain select * from abc_innodb where  b = 2
这个是无法匹配索引的,因为这里只按照b来进行索引查找,而b只是局部有序,只有a才是全局有序
explain select * from abc_innodb where a = 1 and b>2 and c=3
这个是可以匹配的,但是只能部分匹配,a=1肯定是匹配的,但是遇到了b>2,b只是在a的小范围有序
其他a范围的b可能也包含>2的数据,搜易到这里肯定不能继续使用索引了

 注:使用组合索引查询时,mysql会一直向右匹配直至遇到范围查询(>、<、between、like)等就会停止匹配。

总结:如何用好联合索引?

除了在写sql的时候满足最左匹配原则之外,还应该把频繁使用的列、区分度高的列放在前面,频繁使用代表索引利用率高,区分度高代表筛选粒度大,这些都是在索引创建时候需要考虑的优化场景

覆盖索引原则

如何避免回表?

覆盖索引并不是一种索引结构,覆盖索引是一种很常用的优化手段。因为在使用辅助索引的时候,我们只可以拿到相应的主键值,想要获取最终的数据记录,还需要根据主键通过主键索引再去检索,最终获取到符合条件的数据记录。

那么,覆盖索引是指一个索引包含了查询所需的全部列。当查询只需要这些列时,数据库引擎可以直接从索引中读取数据,而不需要访问表中的数据行。这种优化可以减少磁盘 I/O 和提高查询速度。

显然,覆盖索引会增加索引的大小,因为索引中包含更多的列。因此,需要权衡索引的大小和查询性能。

如何使用覆盖索引

CREATE TABLE orders (
    order_id INT PRIMARY KEY,
    customer_id INT,
    order_date DATE,
    total_amount DECIMAL(10, 2),
    status VARCHAR(50)
);

如果你经常执行以下查询:

SELECT order_id, customer_id, order_date FROM orders WHERE status = 'Completed';

你可以用辅助索引把他们三个字段都圈起来(创建多个字段的联合索引),这样就能避免单一的辅助索引致使回表问题

这其实是一种数据库索引调优方案

CREATE INDEX idx_covering ON orders (status, order_id, customer_id, order_date);

在这个索引中,status 是查询条件列,order_idcustomer_idorder_date 是查询结果列。这样,索引包含了查询所需的全部列,可以作为覆盖索引使用。

 一旦创建了覆盖索引,数据库引擎会自动选择使用它来优化查询。你可以通过执行计划来验证是否使用了覆盖索引。例如,在 MySQL 中,可以使用 EXPLAIN 命令来查看查询计划:

EXPLAIN SELECT order_id, customer_id, order_date FROM orders WHERE status = 'Completed';

在查询计划中,如果看到 Using index,则表示使用了覆盖索引

MyIsam索引

对于MyIsam索引我们了解一下就行,主要是了解与InnoDB的区别

以一个简单的user表为例。user表存在两个索引,id列为主键索引,age列为普通索引
CREATE TABLE `user`
(
  `id`       int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(20) DEFAULT NULL,
  `age`      int(11)     DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  KEY `idx_age` (`age`) USING BTREE
) ENGINE = MyISAM
  AUTO_INCREMENT = 1
  DEFAULT CHARSET = utf8;

MyISAM的数据文件和索引文件是分开存储的。MyISAM使用B+树构建索引树时,叶子节点中存储的键值为索引列的值数据为索引所在行的磁盘地址

在磁盘IO次数上,MyIsam要比InnoDB多一次根据地址去磁盘拿数据的操作

在MyISAM存储引擎中,辅助索引和主键索引的结构是一样的,没有任何区别,叶子节点中data阈存储的都是行记录的磁盘地址。 主键列索引的键值是唯一的,而辅助索引的键值是可以重复的