本文章假设读者已了解mysql的索引基础知识,如b+树,聚集索引,并对explain分析结果已了解。以下主要分析mysql的索引选择原理及算法。
Mysql InnoDB的查询一般基于explian所获取的最小rows来选择使用哪一个索引。Rows是一个estimate的值,其估计算法(规则)如下:
1.如果是select c from where a=***,a上有索引。Mysql大概率会使用a上的索引。其rows计算规则为Rows = ((Records_PLeft + Records_P1 + Records_P2 + ... + Records_P8 + Records_PRight)/10)*Page_Num。
每一页的page_size可以通过innodb_index_stats表里的stat_name字段的n_diff_pfx0?和size估算出。如果n_diff_pfx0?为1000000,size为2000,则每页存储500。如果a对应的记录数在500*10=5000内,则此explian获得的rows基本上比较精确,即通过10页的采样全部获取。如果a对应的记录数在5000以上,则rows不准确。参见https://lists.mysql.com/commits/115810,https://www.cnblogs.com/LBSer/p/3333881.html
2.如果是select c from where a=*** order by c ,a和c上都有索引。大概率会走a上索引。因为a的索引是精确查找(explain type 是 const或者ref),用c的索引则是全索引表扫描。
3.如果是select c from where a=*** order by c limit 1 ,a和c上都有索引。这时mysql第一步先用a索引估算出rows值 。然后第二步,尝试再用c的索引来修正rows值:其算法是拿全表的总条数除以第一步算出来的rows,然后乘以limit的值。如全表有1000000条记录,第一步估算出是rows是20000,则rows会变成(1000000/20000)* 1=50。50<20000,则用索引c。如果limit 2,则rows为100,用索引c。一直到估算出来的值大于20000(limit 400左右),才会选用a的索引。见https://bugs.mysql.com/bug.php?id=78325。这里最有意思的是mysql是假设你查询的记录是均匀的分布在索引表里的,所以会有这样算法。
4.如果是select c from where a=*** limit 1,则还是会走a索引,因为mysql不会有上述的第二步。
Mysql 分析工具:Trace介绍: 分析explain整个过程。 set global optimizer_trace='enabled=on'; select c from where a=*** order by c limit 1 select * from information_schema.optimizer_trace;可以分析上步整个trace过程。