第十章:第6节 MySQL进阶篇——组合索引

更新于:2018-02-28 15:20:39

索引如果按照列数去区分,可以分为单列索引和组合索引。超过一个列的我们就称为组合索引或联合索引。主键索引、唯一索引和普通索引都可以建成组合索引。


1.png


上面的“test_tab”表“invest_id”是一个普通索引,同时是一个单列索引。“borrow_id”、“invest_uid”是普通索引,同时也是组合普通索引。


每一条select语句只使用一个索引,可能有些select语句复杂,where过滤条件有多个,这个时候比较适合建一个组合索引。


注:select语句只使用一个索引,大多数情况是这样的,explain语句也有type是index_merge的情况,多个索引结果合并。就算是这样,它也没有合理地使用组合索引高效。


组合索引有些地方需要我们注意,不然建好了索引,查询时也用不到。


全列匹配


1.png


很明显,当按照索引中所有列进行精确匹配(这里精确匹配指“=”或“IN”匹配)时,索引可以被用到。这里有一点需要注意,理论上索引对顺序是敏感的,但是由于MySQL的查询优化器会自动调整where子句的条件顺序以使用适合的索引,例如我们将where中的条件顺序颠倒:


1.png


最左前缀匹配


这个是组合索引最重要的一个特性。


如果表中存在几个字段构成的组合索引,则查找记录时,这个组合索引的最左前缀匹配字段也会被自动作为索引来加速查找。


例如,若为某表创建了3个字段(c1, c2, c3)构成的联合索引,则(c1), (c1, c2), (c1, c2, c3)均会作为索引,(c2, c3)就不会被作为索引,而(c1, c3)其实只利用到c1索引。


1.png


看上面的四条select语句,完全符合“最左前缀匹配”。


提示:where子句中的字段没有先后顺序,比如上图中第二个SQL语句where子句可以这样写,“status = 2 and invest_uid = 24330”,依然能用到“invest_uid”索引。


这个地方也很好理解:


组合索引的结构与电话簿类似,“人名”由“姓”和“名”构成,电话簿首先按“姓”进行排序,然后按“名”对有相同姓氏的人进行排序。如果您知道“姓”,电话簿将非常有用;如果您知道“姓”和“名”,电话簿则更为有用。但如果您只知道“名”不知“姓”,电话簿将没有用处。


所以说创建组合索引时,应该仔细考虑列的顺序。对索引中的所有列执行搜索或仅对前几列执行搜索时,组合索引非常有用;仅对后面的任意列执行搜索时,组合索引则没有用处。


范围查询

看表“test_tab2”

1.png

1.png


范围列可以用到索引,但是范围列后面的列无法用到索引。


如果用between取范围:

1.png


发现索引两个列都用到了。


说明:看起来是用了两个范围查询,但“BETWEEN”实际上相当于“IN”,也就是说uid字段实际是多值精确匹配。可以看到这个查询用到了索引两个列。因此在MySQL中要谨慎地区分多值匹配范围匹配,否则会对MySQL的行为产生困惑。


再看这个:

1.png


正确使用索引了,而select语句却没有用到索引,这是怎么回事?


索引的最主要目的就是快速查找,定位数据。MySQL优化器有时会认为使用索引的成本过高而不会去使用索引。 查询即使使用索引,会导致出现大量的随机IO,相对于从数据记录的第一条遍历到最后一条的顺序IO开销,还要大。


1.png


所以,即便MySQL设置了索引,select语句也不一定就得用,它内部有自己的优化选择。有索引而不去用,大多发生在where条件是范围查询。


其他注意事项


1.png


如果where条件中是OR关系,组合索引不起作用


不只是组合索引,单列索引也不起作用。想让OR使用索引,需保证 OR 两端的条件都存在可以用的索引。


在where子句中有很多运算符,下节我们继续去说下where子句哪些可以用到索引?哪些用不到索引?


本节学习代码》》》