什么是索引
索引是什么?相信大家都用过字典。你是怎么从厚厚的新华字典中找到你需要找到的那个字的呢?又是怎么从一本书中快速定位到你需要的章节?
我们都是通过书中的目录,然后根据目录中的页码定位到我们要的信息。
同样在mysql中也是这样为我们准备了一份目录。当你去通过sql语句查询的时候用不用索引,以及怎么用索引。决定了你的查询所耗费的时间。
在优化我们的应用的时候,首先应该考虑的是使用索引,试图通过其它途径来提高性能则纯粹是在浪费时间。你应该先使用索引最大程度的改进性能,再考虑看看是否有其它的技术可以使用。
所以,索引是什么?—— 是一份经过排序的目录表。
索引的优点
没有索引的数据表:
一个没有索引的数据表,就是一个无序的数据行的集合。我们要从中找到符合条件的一行记录,需要扫描整个表,挨个询问,你是不是?是,留下。不是,找下一个。当然了,通过sql查询出来的数据行也是一个集合。
存在索引的数据表:
先看一张数据表
当我们给Id
添加索引的时候生成的索引文件可能是这个样子的
存在索引的数据表,在查询过程中就不需要再去扫描整张表中的数据行了。比如我们给Id
添加了一个索引,当我们要查询的条件是:Id=13
的时候,我们开始扫描索引,并找到了3条符合条件的记录行。当扫描到达了Id为14的数据行的时候,这个值高于我们搜索的值。由于索引是经过分类的,所以当读取到包含14的记录时,我们就知道不再会有与Id=13
相吻合的数据,从而不再进行扫描。
在上面查询索引文件的时候,使用的是线性扫描,(从第一个,到认为之后不符合条件的最后一个)。另一种方式是定位算法的使用,它们可以不经过线性扫描就可以直接定位到第一个匹配项。从而节省了大量的搜索时间。各种数据库使用各种各样的技术来迅速的找到索引值。而我们只需要知道的是索引这个工具用来干啥,怎么用就行了。
在上面的例子中,可以看出不使用索引跟使用索引进行查询时它们的差别。如果还不能给你震撼感,那么举个例子:
假设你有三张没有索引的数据表:t1、t2、t3。每张表有1000条数据行。我们要找出这三张表中具有相同数值的所有数据行的组合:
SELECT t1.i1,t2.i2,t3.i3
FROM t1 INNER JOIN t2 INNER JOIN t3
WHERE t1.i1 = t2.i2 AND t2.i2 = t3.i3;
这个查询的结果应该有1000个数据行,每行都包含3个相等的数据行。当我们不使用索引来查询的时候,t1表的每行记录我们都需要拿到
t2表中进行全表扫描,同样t2表中符合的记录,我们需要拿到t3表进行全表扫描。
这样子得到符合的记录可能的组合是:1000x1000x1000(10亿)种,比匹配的数目多100万倍。
为数据表编制索引可以很大程度的提高查询的速度,它可以像下面这样处理查询:
1. 从数据表t1中选择第一个数据行,看这个数据行包含什么样的值。
2. 对数据表t2使用索引,直接找到与t1表中相匹配的数据行,类似的对数据表t3使用索引,直接找到与t1表相匹配的数据行
3. 对数据表t1的下一个数据行重复上面的过程,直到检查完t1表中所有的数据行。
使用索引后对t1表还是进行了全表扫描,但是能够对t2、t3数据表带索引搜寻。直接将那些数据行挑选出来,这种查询运行速度比不带索引的查询运行快100万倍。
Mysql使用索引的方式
在查询操作中把与
WHERE
子句所给出的条件相匹配的数据行尽快找出来在关联操作中把与其它数据表里的相匹配的数据行尽快找出来
对于使用
MIN()
或MAX()
这些聚合函数,如果数据列带索引,那么它的最小值和最大值能够快速被找到,而不需要进行全表扫描mysql经常使用索引完成
ORDER BY
和GROUP BY
子句的分类和分组操作。有时候mysql可以通过使用索引避免一个查询扫描完索引文件再去读取数据行。假如你是从
MyISam
数据表的一个有索引的数据列里选取值,而你并不打算读取其它数据列。在这种情况下Mysql从这个索引文件读取索引值时,你实际上已经获取到了这个值。而这个值你本来是应该通过读取数据行才能得到。在这你就不需要读取两次值了。这也是我们为什么不建议写SELECT * FROM tableName
这种语句。第一,如果仅仅需要获取一个字段值,并且这个值有索引,比如用户的Id
主键索引,就不需要查询两次表,直接获取就行了。第二个,对于不必要的字段,比如文章内容这种占用空间比较大的字段会占用较大的网络带宽。
索引在数据库里的存储方式
对于不同的存储引擎,索引实现的细节有所不同。对与MyISAM
数据表来说,数据表的数据行是在数据文件里。而索引值是在索引文件里。一个数据表可以有多个索引,所有的索引都存储在同一个索引文件里。索引文件里的每个索引都是由分类的关键记录数组组成的。这些数组用于快速访问数据表文件。
InnoDB
存储引擎使用的是一个表空间,在这个表空间里,它管理着所有的InnoDB
类型的数据表的数据和索引的存储。我们也可以通过配置使每个使用InnoDB
引擎的数据表创建自己的一个表空间。
索引的缺点
谈到索引的缺点,还是回到刚开始我们介绍什么是索引的时候举的例子。我们有一本书,书中有目录和内容。当我们要往这本书里面新增一篇文章的时候步骤是这样的:
找到这篇文章需要添加的位置,插入进去。
更新这本书的目录,使读者可以快速的定位到这篇文章。
此时你会发现一个问题,当你新增的文章越来越多的时候,你的目录也会变得越来越厚。对了~索引会占用一定的磁盘空间。
另外的是,每次新增一篇文章你都得更新下目录。会占用一定的时间。索引也一样,所以当涉及到对有索引的数据表进行插入、删除、更新,等操作的时候,索引会降低这些操作的性能。之所以会出现这种情况是由于对一条数据行进行插入操作,不仅要修改数据表中的数据行,还要求所修改的数据行的索引要做出改变。一个数据表有越多的索引,需要做出的改变就越多,平均性能下降就越多。
绝大多数表都是读操作多过于写操作,但对那些写操作次数比较多的表来说,索引更新的开销可能会非常大。
对于
MyISAM
数据表来说,大量的索引一个数据表有可能是索引文件比数据文件更快的达到它的最大尺寸。存储在
InnoDB
共享表空间里的全部InnoDB
数据表分享一个存储空间。添加索引会使表空间里用于存储的空间更快的减少。和MyISAM
数据表不同的是InnoDB
数据表的共享表空间不受操作系统文件尺寸的限制。如果配置成每个使用InnoDB
引擎的数据表使用自己的表空间,数据和索引保存在一个文件里,增加索引将会导致数据表的尺寸更快的逼近文件的最大长度。
尽管有上面所提到的时间和空间上的缺点,但是你见过一本书没有目录吗?所以在使用索引的时候需要均衡的考虑,是不是非用不可。可以多进行几次基准测试,当然了,如果时间允许的话。
挑选索引
尽量为用来搜索、分类或分组的数据列编制索引
你有一张数据表,该怎么具体为哪一个字段添加索引,能够让mysql的优化器找到它,并使用它?
尽量为用来搜索、分类或分组的数据列编制索引。不要为作为显式或输出的数据列编制索引。也就是说最适合有索引的数据列
是那些在:
1. 出现在 WHERE 子句中的数据列
2. 在联结子句中出现的数据列 如:SELECT * FROM aTable INNER JOIN bTable ON aTable.Id = bTable.Id;
3. 在 ORDER BY 或 GROUP BY 子句中出现的数据列。
根据 SELECT 子句中出现的数据列仅仅用来输出显式的字段最好不要有索引。
考虑数据列的维度
数据列的维度等于它所容纳的非重复值的个数。比如说有个数据列里面的值分别是:1,5,19,75,5,1
。它的维度就是 4
(去掉重复值后)。数据列的维度的最大值等于表里数据行的个数。数据列的维度值越高包含的重复值就越少,索引的使用效果也就越好。
以前在设计表的时候我也纠结过到底要不要给数据表中的 Status(状态)
加索引。因为这个字段是经常在WHERE
语句中出现的。而实际上这种可以用ENUM
数据类型的字段当他在数据表中的数据行出现频率超过30%
后mysql的查询优化器通常会跳过索引,而进行全表扫描。现在的优化器更复杂,能够把其它因素考虑进来。百分比不再是mysql决定进行一次全表扫描而不使用索引的唯一依据了。所以当你认为该数据列的维度确实不是太底的时候可以用DESC
或者EXPLAIN
与验证下优化器到底有没有使用到索引。
关于复合索引
当你创建了一个n
个数据列的复合索引时,实际上就创建了mysql能够使用的n
个索引。怎么说呢?比如:
KEY `indexName` (`column1`,`column2`,`column3`);
注意索引的顺序,在查询中能够使用的索引顺序如下:
column1, column2, column3
column1, column2
column1
mysql不能使用没有包含最左边索引字段的索引(column1
)。比如:column2,column3
。猜想下如果使用column1,column3
呢?mysql 能不能用到索引?答案是可以的,但也只用到了column1
这个索引,column3
这个索引时用不到的。也就是说mysql能够使用到column1
去缩小匹配的范围,但是这个索引不能用于这个值的组合。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。