索引的优缺点
优点
-
索引大大减小了服务器需要扫描的数据量
-
索引可以帮助服务器避免排序和临时表
-
索引可以将随机IO变成顺序IO
-
索引对于InnoDB(对索引支持行级锁)非常重要,因为它可以让查询锁更少的元组。在MySQL5.1和更新的版本中,InnoDB可以在服务器端过滤掉行后就释放锁,但在早期的MySQL版本中,InnoDB直到事务提交时才会解锁。对不需要的元组的加锁,会增加锁的开销,降低并发性。 InnoDB仅对需要访问的元组加锁,而索引能够减少InnoDB访问的元组数。但是只有在存储引擎层过滤掉那些不需要的数据才能达到这种目的。一旦索引不允许InnoDB那样做(即索引达不到过滤的目的),MySQL服务器只能对InnoDB返回的数据进行WHERE操作,此时,已经无法避免对那些元组加锁了。如果查询不能使用索引,MySQL会进行全表扫描,并锁住每一个元组,不管是否真正需要。
-
- 关于InnoDB、索引和锁:InnoDB在二级索引上使用共享锁(读锁),但访问主键索引需要排他锁(写锁)
缺点
- 虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。因为更新表时,MySQL不仅要保存数据,还要保存索引文件。
- 建立索引会占用磁盘空间的索引文件。一般情况这个问题不太严重,但如果你在一个大表上创建了多种组合索引,索引文件的会膨胀很快。
- 如果某个数据列包含许多重复的内容,为它建立索引就没有太大的实际效果。
- 对于非常小的表,大部分情况下简单的全表扫描更高效;
索引只是提高效率的一个因素,如果你的MySQL有大数据量的表,就需要花时间研究建立最优秀的索引,或优化查询语句。
因此应该只为最经常查询和最经常排序的数据列建立索引。
MySQL里同一个数据表里的索引总数限制为16个。
索引存储类型
B-Tree索引
InnoDB使用的是B+Tree。
B+Tree:每一个叶子节点都包含指向下一个叶子节点的指针,从而方便叶子节点的范围遍历。
B-Tree通常意味着所有的值都是按顺序存储的,并且每一个叶子页到根的距离相同,很适合查找范围数据。
B-Tree可以对<,<=,=,>,>=,BETWEEN,IN,以及不以通配符开始的LIKE使用索引。
索引查询
可以利用B-Tree索引进行全关键字、关键字范围和关键字前缀查询,但必须保证按索引的最左边前缀(leftmost prefix of the index)来进行查询。
假设有如下一个表:
CREATE TABLE People (
last_name varchar(50) not null,
first_name varchar(50) not null,
dob date not null,
gender enum('m', 'f') not null,
key(last_name, first_name, dob)
);
其组合索引包含表中每一行的last_name、first_name和dob列。其结构大致如下:
按索引的最左边前缀(leftmost prefix of the index)来进行查询:
-
查询必须从索引的最左边的列开始,否则无法使用索引。例如,你不能直接利用索引查找在某一天出生的人。
-
不能跳过某一索引列。例如,你不能利用索引查找last name为Smith且出生于某一天的人。
-
存储引擎不能使用索引中范围条件右边的列。例如,如果你的查询语句为WHERE last_name=“Smith” AND first_name LIKE ‘J%’ AND dob=‘1976-12-23’,则该查询只会使用索引中的前两列,因为LIKE是范围查询。
-
匹配全值(Match the full value):对索引中的所有列都指定具体的值。例如,上图中索引可以帮助你查找出生于1960-01-01的Cuba Allen。
-
匹配最左前缀(Match a leftmost prefix):你可以利用索引查找last name为Allen的人,仅仅使用索引中的第1列。
-
匹配列前缀(Match a column prefix):例如,你可以利用索引查找last name以J开始的人,这仅仅使用索引中的第1列。
-
匹配值的范围查询(Match a range of values):可以利用索引查找last name在Allen和Barrymore之间的人,仅仅使用索引中第1列。
-
匹配部分精确而其它部分进行范围匹配(Match one part exactly and match a range on another part):可以利用索引查找last name为Allen,而first name以字母K开始的人。
-
仅对索引进行查询(Index-only queries):如果查询的列都位于索引中,则不需要再多一次I/O回读元组。(覆盖索引:索引的叶子节点中已经包含要查询的数据,那么就没有必要再回表查询了,如果索引包含满足查询的所有数据,就称为覆盖索引。)
索引排序
也可以利用B-Tree索引进行索引排序(对查询结果进行ORDER BY),必须保证ORDER BY按索引的最左边前缀(leftmost prefix of the index)来进行。
MySQL中,有两种方式生成有序结果集:
- 使用filesort
- 按索引顺序扫描
如果explain出来的type列的值为“index”,则说明MYSQL使用了索引扫描来做排序。
按索引顺序扫描:
可以利用同一索引同时进行查找和排序操作:
- 当索引的顺序与ORDER BY中的列顺序相同,且所有的列是同一方向(全部升序或者全部降序)时,可以使用索引来排序。
- ORDER BY子句和查询型子句的限制是一样的:需要满足索引的最左前缀的要求,有一种情况下ORDER BY子句可以不满足索引的最左前缀要求,那就是前导列为常量时:WHERE子句或者JOIN子句中对前导列指定了常量。
- 如果查询是连接多个表,仅当ORDER BY中的所有列都是第一个表的列时才会使用索引。其它情况都会使用filesort文件排序。
使用filesort:
当MySQL不能使用索引进行排序时,就会利用自己的排序算法(快速排序算法)在内存(sort buffer)中对数据进行排序;
如果内存装载不下,它会将磁盘上的数据进行分块,再对各个数据块进行排序,然后将各个块合并成有序的结果集(实际上就是外排序,使用临时表)。
对于filesort,MySQL有两种排序算法:
按索引顺序扫描:
可以利用同一索引同时进行查找和排序操作:
- 当索引的顺序与ORDER BY中的列顺序相同,且所有的列是同一方向(