MySQL索引学习笔记03——高性能的索引策略(多列索引&索引列顺序)

1. 多列索引

多列索引不是为每个列创建独立的索引,或者按照错误的顺序创建多列索引;在多个列上建立独立的单列索引大部分情况下并不能提高MySQL的查询性能。MySQL 5.0及以上版本引入了一种叫“索引合并”(index merge)的策略,一定程度上可以使用表上的多个单列索引来定位指定的行

早版本的MySQL只能使用其中某一个单列索引,但哪一个独立的单列索引是非常有效的。如表film_actor在字段film_id和actor_id上各有一个单列索引,但对于其查询WHERE条件,这两个单列索引都不是好的选择:

mysql> SELECT film_id, actor_id FROM sakila.film_actor
	-> WHERE actor_id = 1 OR film_id = 1;

老MySQL版本中,会对这个查询使用全表扫描,除非改写成下面两个查询UNION的方式:

mysql> SELECT film_id, actor_id FROM sakila.film_actor WHERE actor_id = 1
        -> UNION ALL
        -> AND actor_id <> 1;

在索引合并中,查询能同时使用这两个单列索引进行扫描,再结果进行合并。该算法有三个变种:OR条件的联合(union),AND条件的相交(intersection),组合前两种情况的联合及相交

索引合并有时是一种优化的结果,但更多时候说明了表上的索引建得很糟糕

  • 当服务器对多个索引做相交操作时(通常有多个AND条件),通常意味着需要一个包含所有相关列的多列索引,而不是多个独立的单列索引
  • 当服务器需要对多个索引做联合操作时(通常有多个OR条件),通常需要耗费大量CPU和内存资源在算法的缓存、排序和合并操作上。特别是当其中有些索引的选择性不高,需要合并扫描返回的大量数据的时候
  • 更重要的是,优化器不会把这些计算到“查询成本”(cost)中,优化器只关心随机页面读取。这会使得查询的成本被“低估”,导致该执行计划还不如直接全表扫描,从而消耗更多的CPU和内存资源,还可能会影响查询的并发性

2. 选择合适的索引列顺序

当不考虑排序和分组时,将选择性最高的列放在前面通常是很好的,这时索引的作用只是用于优化WHERE条件的查找。这样确实能够最快地过滤出需要的行,对于在WHERE子句中只使用了索引部分前缀列的查询来说选择性也更高。但性能不只是依赖于所有索引列的选择性(整体基数),也和查询条件的具体值有关(值的分布)。

这和前面介绍的选择前缀的长度需要考虑的地方一样。我们要根据那些运行频率最高的查询来调整索引列的顺序,使索引的选择性最高,如:

SELECT * FROM payment WHERE staff_id = 2 AND customer_id = 584;

这里应该创建一个(staff_id,customer_id)索引还是应该颠倒一下顺序?我们通过下面这个查询可以得到customer_id的条件值更小(选择性更高),应该放在前面

mysql> SELECT SUM(staff_id = 2), SUM(customer_id = 584) FROM payment\G
************************************
SUM(staff_id = 2): 7992
SUM(customer_id = 584): 30

但是下面对于这个customer_id的条件值,对应的staff_id列的选择性却更高

mysql> SELECT SUM(staff_id = 2) FROM payment WHERE customer_id = 584\G
************************************
SUM(staff_id = 2): 17

说明查询的结果非常依赖于选定的具体值,这种情况下,可能对其他一些条件值的查询不公平,如服务器的整体性能变得更糟,或其他某些查询的运行变得不如预期


该学习笔记学习自《高性能MySQL》/(美)Schwartz,(美)Zaitsev,(美)Tkachenko著 宁海元等译

你可能感兴趣的:(数据库,自学,算法,mysql,数据库,索引)