MySQL B+ 树的索引-联合索引
联合索引
联合索引是指对表上的多个列进行索引。联合索引的创建方法与单个索引创建的方法一样,不同之处是有多个索引列。
如下图: 创建一个two_key 表,并且id_fid是联合索引,联合的列是(id, fid)
那么联合索引的内部是什么样的呢?
从本质上来说,联合索引也是一颗B+树,不同的是联合索引的键值的数量和不是1,而是大于等于2。接着来讨论两个整数型列组成的联合索引,假设两个键值的名称为id, fid 如图 [多个键值的B+树]:
从图中可以看到多个键值的B+树情况,键值都是排序的。通过叶子节点可以逻辑上顺序读取所有数据,就上面图中所示,即为(1,1)、(1、2)、(2、1)、(2、4)、(3、1)、(3、2),数据是按照(id, fid)的顺序进行存放。
所以,对于查询 select * from table two_key where id = XXX and fid = XXX, 像这样的操作显然是可以使用(id, fid)这联合索引的。对于单个列id的查询 select * from two_key where id = XXX 也是可以使用这个联合索引,但是对于fid列的查询 select * from two_key where fid = XXX 则是不能使用这个B+树的联合索引,因为 叶子节点中的fid的值为:1、2、1、4、1、2 显然不是排序的,因此对于fid的查询是使用不到(id, fid )这个联合索引的。
使用联合索引的第二个好处是对已经对第二个键值进行了排序处理。 这里的情况是确定了第一个键,这种情况,对于第一个键相同的记录来说,查询的结果是第二个键是已经进行了排序。我们举一个栗子吧! 例如:有些应用程序都需要查询某个用户的购物情况,并按照时间进行排序,最后取出最近三次的购买记录,这时使用联合索引就可以避免多一次排序操作,因为索引本身在叶子节点已经排序了。
那么来看一个栗子吧:
创建一个buy 表,里面的字段如下,
向表中插入数据,并且修改表,将创建userid索引和(userid, buy_date)联合索引。
如果执行userid进行查询 select * from buy where userid = 2
在这里使用explain对这条语句进行分析,如图 [ 使用explain分析select查询 ]:
从分析中可以看出在possible_keys中有两个索引可以使用,分别是userid 和 userid_2(即为(userid, buy_date)联合索引),但是优化器最终选择的是userid_2.
但是选择对单列userid进行查询的时候,查询优化器使用的索引是userid,因为查询userid,userid本身就是索引,那么就没有必要使用联合索引了。如下图 [ 单独查询userid使用的索引 ]:
继续看,如果查询userid为2的最近2次的购买记录,那么执行的效果是什么呢? 看下图 [ 查询userid=2最近两次购买请求使用的索引 ]:
对于上述SQL语句既可以使用userid索引,也可以使用联合索引(userid,buy_date),但是优化器使用的是userid_2 ((userid, buy_date)),因为在这个联合索引中buy_date是已经排序好的了,根据这个索引取出的数据,无须再进行第二次的排序操作了。
再来看一个操作,这次是查询所有的信息,并且根据buy_date进行排序,那么我们看是什么执行结果呢?马上放图 [ 不使用联合索引,进行再次排序 ]:
从上图可以知道,在possible_key中并没有可选择的索引,但是优化器最红选择了userid_2 ,因为毕竟有些记录是已经排好序的,但是全部数据并不是有序的,所以从Extra中可以看出多出了一个 Using filesort, 说明使用了第二次排序,即对索引userid_2查询出来的结果进行再次排序。
小小的总结: 联合索引(a, b)其实是根据a、b列进行排序,因此下列语句可以直接使用联索引得到结果:
select * from table where a= XXX order by b
然而对于联合索引(a, b,c)来说,下列语句同样可以直接通过联合索引得到结果:
select … from table where a=XXX order by b
select … from table where a=XXX and b =XXX order by c
但是对于下面的语句。联合索引不能直接得到结果,其中还需要执行一次filesort排序操作,因为(a, c)并未排序。
select … from tabel where a=XXX order by c