MySQL索引那些事

概述

上一篇文章《一条sql语句在mysql中是如何执行的》咱们聊到了sql语句内部的执行,包括InnoDB引擎是如何支持事务的,如何作到能够备份恢复的,那么今天咱们来聊一聊MySql索引的那些事,在这篇文章中,我会主要聊聊InnoDB下索引的数据结构,索引如何起做用的,如何更好的利用索引提升效率。mysql

1、什么是索引

数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据。就像咱们之前用的新华字典的目录同样,能帮助咱们快速查询到某一个字。sql

2、索引的分类

分类角度 索引名称
数据结构 B+树,Hash索引,R-Tree等
存储层面 聚簇索引,非聚簇索引
逻辑层面 主键索引,普通索引,复合索引,惟一索引,空间索引等

3、索引实例分析(以InnoDB为例)

3.1 InnoDB下索引的结构

InnoDB下,表都是根据主键顺序以索引的形式存放的,这种数据存储方式也被称为聚簇索引,“聚簇”就是表示数据行和相邻的键值紧凑的存储在一块儿,也就是数据行其实是存储在索引的叶子页中。咱们建立一张表来实际说明下InnoDB下的索引结构,建表语句以下:数据库

create table person(
id int primary key, 
age int not null
index (age)engine=InnoDB;

复制代码

而后咱们插入五条数据分别为(1,15),(2,17),(6,20),(10,18),(19,21),索引的树结构以下:bash

上图中展现了两部份内容,第一个图为聚簇索引(主键索引)的内容,能够看到,数据按照Id的大小排序,对应的索引会包含该索引的整行数据。服务器

第二个图展现了用age作索引的索引结构图,也就是非聚簇索引(非主键索引),能够看到索引以年龄排序,可是和主键索引不一样的是,年龄索引对应的倒是Id,因此咱们能够知道非主键索引记录的内容就是主键索引的值。数据结构

这里可能有同窗会有疑问,若是我建表的时候没有指定主键的话,索引结构又是如何的呢?其实在InnoDB中,若是没有定义主键,那么他会选择一个惟一的非空索引代替。若是没有这样的索引,那么他会隐式的定义一个主键来做为聚簇索引。因此不管你是否设置主键,InnoDB仍是会帮你知足以上图的形式来索引数据。接下来咱们分析下索引查询的流程。post

3.2 索引查询分析

假设咱们执行一条查询语句 select * from person where ID = 6,由于直接使用的是主键ID查询,因此就会用主键索引,因为主键索引直接关联了整行全部数据,因此,引擎只要执行一次就能查询出结果。性能

若是执行的sql语句是非主键索引spa

select * from person where age = 183d

上述语句会走age的普通索引,索引先根据age搜索等于18的索引记录,找到ID=10的记录,而后再到主键索引搜索一次,而后拿出须要查询的数据。

从普通索引查出主键索引,而后查询出数据的过程叫作回表。因为回表须要多执行一次查询,这也是为何主键索引要比普通索引要快的缘由,因此,咱们要尽可能使用主键查询。

3.3 覆盖索引

咱们一般建立索引的依据都是根据查询的where条件,可是这只是咱们一般的作法,咱们根据上面的分析能够知道,若是要想查询效率高,第一,使用主键索引,第二,避免回表,也就是尽量的在索引中就能获取想要的数据。若是一个索引包含了须要查询的字段,那么咱们就叫作“覆盖索引”。

那么如何创建一个覆盖索引呢?答案是经过联合索引来实现,经过联合索引的字段来覆盖要查询的字段,从而达到索引覆盖的效果。

咱们把上面的建表语句改造下,来分析下如何实现覆盖索引。

CREATE TABLE `person` (
  `id` int(11) NOT NULL,
  `age` int(11) DEFAULT NULL,
  `name` varchar(20) DEFAULT NULL,
  `sex` varchar(1) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `name_age` (`name`,`age`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

复制代码

上面我建立了一个name和age的联合索引,索引结构图表示以下:

咱们根据图能够知道,联合索引是和建立索引字段顺序有关的,上面这个例子就是先以name排序,而后name相同再以age为标准排序。那么咱们建表后该如何达到覆盖索引的效果呢?相信有些同窗已经知道了怎么写sql能够达到覆盖索引效果,sql以下:

select name,age from person where name = "Barry"

由于咱们须要查询的字段name和age,都在索引中能够直接查询到了,因此不须要查找到主键ID,而后再回表了。

看到这里,确定有同窗会说,既然这样的话,我把全部须要查询的字段组合都建上联合索引不就好了吗?答案是:不行。由于索引也是须要消耗空间的,并且维护索引也是须要成本的,这一点我会在后面的优缺点中提到。那么有没有别的方式能够尽量的实现不回表的效果呢?这里咱们就要引入MySql的最左前缀原则了。

什么叫最左前缀原则呢?就是在索引的匹配中,能够以索引的最左N个字段,也能够是字符串索引的最左N个字符。好比在上图中,要查询以A开头的名字,查询语句就是

select name from person where name like 'A%'

这个时候就能够知足最左前缀规则来使用索引查询了,这里就会依赖索引查询到第一个首字母是A的名字,而后向后遍历,直到不知足条件为止。

那么最左N个字段是什么意思呢?意思就是索引(name,age),能够直接利用 name来当作单独索引使用,能够只使用联合索引的部分字段,可是必须是顺序一致,好比索引(a,b,c),若是要想使用最左前缀规则,可使用索引a,ab。

咱们也能够利用该规则来少维护一个或多个索引,好比咱们须要 a,ab,abc的查询,那就只须要(a,b,c)联合索引就知足要求了。

3.4 索引下推

在MySql 5.6版本中引入了一个新特性,叫作“索引条件推送(index condition pushdown)”,这也称为索引下推。那么索引下推是这个什么东东呢?其实从“索引条件推送”这个名字就能够代表,这个特性是能够在索引中的字段进行条件判断,而后过滤不知足条件的记录,减小回表的次数。

好比以上图中的数据为准,sql以下:

select * from person where name like 'A%' and age =19;

那么若是没有索引下推的状况下,首先会根据索引查询出名字以A开头的全部记录,而后查询出ID,而后回表去查询对应的ID记录,最后再判断age=19,返回知足条件的语句。由于知足A开头的记录有2条,因此这种状况下,会回表2次。

在索引下推状况下,InnoDB会在索引内部直接判断age=19是否知足条件,过滤掉不知足条件的记录,因此只返回了一条,也就是只须要回表一次。从而提升了性能。

3.5 索引的优势与缺点

说了这么多关于索引的内容,咱们来谈谈索引的优缺点。

优势:

  • 减小服务器须要扫描的数据量
  • 索引能够帮助服务器避免排序和临时表
  • 索引能够将随机IO变为顺序IO

缺点

  • 索引会占用额外的存储空间
  • 索引的维护须要必定的成本,插入数据后须要保证原来的索引有序,因此也会影响必定的数据库性能。

5、总结

这篇博文我主要说了,索引的定义,索引的分类,索引按照不一样的角度能够分为常见的哪几种。而后我重点说了在InnoDB下索引的索引的数据结构。 主键索引和非主键索引的区别就是查询主键索引能够直接返回数据,非主键索引须要先查询出主键ID,而后再查询出数据,这个过程就叫作回表。咱们能够经过覆盖索引减小回表的次数,从而达到提升性能的效果。在mysql5.6之后,InnoDB能够支持索引下推,在使用联合索引的时候,若是能够在索引判断条件,那么就在索引中过滤不知足条件的行,从而减小回表次数。

6、参考

  • 《高性能MySql》第3版
  • 《MySql45讲》专栏
相关文章
相关标签/搜索