# mysql索引

## 1. 索引是什么

索引其实也是一张表，该表保存了主键与索引字段，并指向实体表的记录。

## 2. 索引的好处：

创建索引可以大大提高系统的性能。

1. 加快mysql的检索速度，这也是建立索引的主要原因；
2. 通过创建唯一性索引，可以保证数据库中每条记录的唯一性；
3. 可以加速表和表之间的连接，特别是在实现数据的参考完整性方面特别有意义；
4. 在使用分组和排序子句进行数据检索时，同样可以显著减少查询中分组和排序的时间；
5. 通过使用索引，可以在查询的过程中，使用优化隐藏器，提高系统的性能。

## 3. 索引的坏处：

1. 降低更新表的速度，因为对表进行insert、update、delete时，mysql不仅要保存数据，还要更新索引文件；
2. 建立索引会占用磁盘空间的索引文件，一般情况这个问题不太严重，但如果你在一个大表上创建了多种组合索引，索引文件的会增长很快。

## 4. 索引的分类：

1. **普通索引：** 分为单列索引和组合索引，单列索引，即一个索引只包含一个列，多列索引，即一个索引包含多个列；
2. **唯一索引：** 索引列的值必须唯一，但允许有空值，如果是组合索引，则列值的组合必须唯一；
3. **主键索引：** 一个表的主键就是一种特殊的唯一索引，不能有空值，一个表只能有一个主键；
4. **组合索引：** 指在多个字段上创建的索引，只有在查询条件中使用了创建组合索引时的第一个字段，索引才会被使用。使用组合索引时遵循最左前缀原则。

   ```
   最左前缀：
   定义：最左前缀原则指的的是在where 子句中一些条件或表达式中出现的列的顺序要保持和多索引的一致或以多列索引顺序出现，只要 出现非顺序出现、断层都无法利用到多列索引。

   举例说明：加入有一个多列索引(username,password,age)，当三列在where中出现的顺序如(username,password,age)、 (username,password)、(username)才能用到索引，如下面几个顺序(password,age)、(passwrod)、(age)，这三者不 从username开始；(username,age)，出现断层，少了password，都无法利用到索引。因为B+tree多列索引保存的顺序是按照索引创建的顺序，检索索引时按照此顺序检索。
   ```
5. **全文索引：** 主要用来查找文本中的关键字，而不是直接与索引中的值相比较。fulltext索引跟其它索引大不相同，它更像是一个搜索引擎，而不是简单的where语句的参数匹配。fulltext索引配合match against操作使用，而不是一般的where语句加like。它可以在create table，alter table ，create index使用，不过目前只有char、varchar，text 列上可以创建全文索引。值得一提的是，在数据量较大时候，先将数据放入一个没有全文索引的表中，然后再用CREATE index创建fulltext索引，要比先为一张表建立fulltext然后再将数据写入的速度快很多。

```sql
   -- 全文索引查询的例子
   select * from books where match(title,content) against('java');
```

### 聚簇索引和非聚簇索引

**聚簇索引：** 数据行的物理顺序与列值的逻辑顺序相同，一个表中只能拥有一个聚簇索引；

**非聚簇索引：** 该索引的的逻辑顺序与磁盘上行的物理存储顺序不同，一个表中可以拥有多个非聚簇索引。

* 主键索引属于聚簇索引，普通索引，唯一索引和全文索引都属于非聚簇索引
* 聚簇索引的查询效率比非聚簇索引的效率要高，但是需要写入性能不高，因为要移动对应数据的物理位置，所以一般建议主键递增的插入方式。

## 5. 哪些情况不适合加索引

1. 对于那些在查询中很少使用或者参考的列不应该创建索引。这是因为，既然这些列很少使用到，因此有索引或者无索引，并不能提高查询速度。相反，由于增加了索引，反而降低了系统的维护速度和增大了空间需求。
2. 对于那些只有很少数据值的列也不应该增加索引。这是因为，由于这些列的取值很少，例如人事表的性别列，在查询的结果中，结果集的数据行占了表中数据行的很大比例，即需要在表中搜索的数据行的比例很大。增加索引，并不能明显加快检索速度。
3. 对于那些定义为text, image和[bit数据类型](https://www.baidu.com/s?wd=bit%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B\&from=1012015a\&fenlei=mv6quAkxTZn0IZRqIHckPjm4nH00T1dWrjbknjubny7WuAR3Pj6L0ZwV5Hcvrjm3rH6sPfKWUMw85HfYnjn4nH6sgvPsT6KdThsqpZwYTjCEQLGCpyw9Uz4Bmy-bIi4WUvYETgN-TLwGUv3EPHR1PjDknjbv)的列不应该增加索引。这是因为，这些列的数据量要么相当大，要么取值很少。
4. 当修改性能远远大于检索性能时，不应该创建索引。这是因为，修改性能和检索性能是互相矛盾的。当增加索引时，会提高检索性能，但是会降低修改性能。当减少索引时，会提高修改性能，降低检索性能。因此，当修改性能远远大于检索性能时，不应该创建索引。
5. 如果每次都需要取表的所有记录；
6. 对于记录比较少的表。

## 6. 索引的数据结构

InnoDB和MyISAM数据存储引擎中使用B+树来保存索引，但是这两种引擎的实现方式不同。

InnoDB存储引擎支持以下几种索引：

* B+ 树索引（B+树就是平衡查找树）
* 全文索引
* 哈希索引

InnoDB支持的哈希索引是自适应的，会根据表的使用情况自动为表生成哈希索引，不能人为干预是否在一张表中生成哈希索引。InnoDB存储引擎会监控对表上各索引页的查询，如果观察到哈希索引可以带来速度提升，则建立哈希索引，称之为自适应哈希索引。

B+树索引并不能找到一个给定键值得具体行，B+树索引能找到的只是被查找数据行所在的页，然后数据库通过把页读入内存，再在内存中进行查找，最后得到想要查找的数据。

B+树是为磁盘或其他直接存取辅助设备设计的一种平衡查找树。在B+树中，所有记录节点都是按键值的大小顺序存放在同一层的叶子节点上，由各叶子节点指针进行连接。

## 7. B+树和B树的区别

b树（balance tree）和b+树应用在数据库索引，可以认为是m叉的多路平衡查找树，但是从理论上讲，二叉树查找速度和比较次数都是最小的，为什么不用二叉树呢？ 因为我们要考虑磁盘IO的影响，它相对于内存来说是很慢的。数据库索引是存储在磁盘上的，当数据量大时，就不能把整个索引全部加载到内存了，只能逐一加载每一个磁盘页（对应索引树的节点）。所以我们要减少IO次数，对于树来说，IO次数就是树的高度，而“矮胖”就是b树的特征之一，它的每个节点最多包含m个孩子，m称为b树的阶，m的大小取决于磁盘页的大小。

**B树定义：**

描述一颗B树时需要指定它的阶数，阶数表示了一个节点最多有多少个孩子节点，一般用字母m表示阶数。

* 每个节点最多有m-1个**关键字**（可以存在键值对）。
* 根节点最少可以只有1个**关键字**。
* 非根节点至少有m/2个**关键字**。
* 每个节点中的关键字都按照从小到大的顺序排列，每个关键字的左子树中的所有关键字都小于它，而右子树中的所有关键字都大于它。
* 所有叶子节点都位于同一层，或者说根节点到每个叶子节点的长度都相同。
* 每个节点都存有索引和数据，也就是对应的key和value。

所以，根节点的**关键字**数量范围：`1 <= k <= m-1`，非根节点的**关键字**数量范围：`m/2 <= k <= m-1`。举个例子来说明一下上面的概念，比如这里有一个5阶的B树，根节点数量范围：1 <= k <= 4，非根节点数量范围：2 <= k <= 4。

**B+树定义：**

前两个是相同点，后4个是不同点：

* 根节点至少一个元素
* 非根节点元素范围：m/2 <= k <= m-1
* B+树有两种类型的节点：内部结点（也称索引结点）和叶子结点。内部节点就是非叶子节点，内部节点不存储数据，只存储索引，数据都存储在叶子节点。
* 内部结点中的key都按照从小到大的顺序排列，对于内部结点中的一个key，左树中的所有key都小于它，右子树中的key都大于等于它。叶子结点中的记录也按照key的大小排列。
* 每个叶子结点都存有相邻叶子结点的指针，叶子结点本身依关键字的大小自小而大顺序链接，便于区间查找和遍历，
* 父节点存有右孩子的第一个元素的索引。

## 8. 用不到索引的情况

1. 如果MySQL估计使用索引比全表扫描更慢，则不使用索引。例如，如果列key均匀分布在1和100之间，下面的查询使用索引就不是很好：`select * from table_name where key>1 and key<90;`
2. 如果条件中有or，即使其中有条件带索引也不会使用索引(这也是为什么SQL语句中尽量少用or的原因)，例如：`select * from table_name where key1='a' or key2='b';`如果在key1上有索引而在key2上没有索引，则该查询也不会走索引；(**注意：要想使用or，又想让索引生效，只能将or条件中的每个列都加上索引。**)
3. 不符合最左前缀的查询不会用到组合索引；
4. 如果like是以‘%'开始的，则该列上的索引不会被使用。例如`select * from table_name where key1 like '%a'`；该查询即使key1上存在索引，也不会被使用；
5. 如果列为字符串，则where条件中必须将字符常量值加引号，否则即使该列上存在索引，也不会被使用。例如,`select * from table_name where key1=1;`如果key1列保存的是字符串，即使key1上有索引，也不会被使用；
6. 索引列上做运算，也会导致不使用索引。例如，select \* from a where b+1 >10;不会使用索引；

> 参考：
>
> 《MySQL技术内幕》
>
> <http://www.runoob.com/mysql/mysql-index.html>
>
> <https://blog.csdn.net/superit401/article/details/51291603>
