在优化复杂sql的过程中,避开不了建立联合索引,一个复杂的sql语句,where 条件涉及多个列,比较条件也多样化,有等值比较,有范围查询,有不等比较等等,有的列区分度高,有的列区分度很低。最后还有排序字段。
那我们应该怎么建立联合索引才是最优解呢?
我们假设有一个t1表
create table t1
(
c1 int,
c2 int,
c3 int,
c4 int,
c5 int,
c6 int
.....
)
假如 我们有如下sql语句
select c1 from t1 where c2 = xx order by c3
我们假设c2列区分度还行。那我们该怎么建立索引呢?
我们需要建立一个idx1(c2,c3,c1) 的联合索引
且一定必须是这个顺序。
先谈谈为什么必须是这个顺序
mysql联合索引遵守最左匹配原则
了解最左匹配原则可以参见官方文档
而且上述索引,满足最牛的三星索引规则。
我简单说一下三星索引规则
三星索引规则:
一星索引: 列上有良好的区分度
二星索引: 索引的排序即语句的排序(mysql 不需额外花费排序代价)
三星索引: 索引覆盖,不需要回表获取额外数据
如果一个索引满足上述三条规则。则是最好的索引了,在亿级表中查询也是非常非常快的。
但我们实际的sql会大大复杂于上面的例子。
那我们应该怎么建立联合索引呢?
在mongodb官方文档的最佳实践中,有对建多列索引的建议
并给出一个好记的规则。我们来了解一下
use compound indexes
compound indexes are indexes composed of several different fields. for example, instead of having one index on “last name” and another on “first name”, it is typically most efficient to create an index that includes both “last name” and “first name” if you query against both of the names. our compound index can still be used to filter queries that specify the last name only.
follow the esr rule
for compound indexes, this rule of thumb is helpful in deciding the order of fields in the index:
first, add those fields against which equality queries are run.
the next fields to be indexed should reflect the sort order of the query.
the last fields represent the range of data to be accessed.
上面提到了一个原则:
esr原则
首先添加等值字段
其次添加排序字段
最后添加范围查询字段
如第一个例子中 其实也是遵守了esr原则: c2字段是等值字段 c3字段是排序字段
为什么要加c1字段呢,就是为了实现三星索引中的不回表。
所以我自己将规则改了一下。
我们联合索引该用什么规则呢? ---- esro
esro规则
e:首先添加等值字段
s:其次添加排序字段
r:再次添加范围查询字段
o:最后添加select语句的其它字段 如select 处的。join处的 where 条件没有用剩下的字段
esro规则并不是代表联合索引必得要这么做。而是给出了指导
比如select 绝大部份字段 这里明显添加o 建立索引就不合适了。
有了三星索引理论 esro规则为理论基础,我们来看一下一个复杂查询该怎么建索引
假期有一个表有50列
create table t2
(
c1 int,
c2 varchar,
c3 int,
c4 varchar,
....
c50 int,
primary key(c50)
)
有如下一个复杂sql
select t2.c1,t2.c4,t2.c9,t2.c50,t3.xx from t2
left join t3 on t2.c40 = c3.c1
where t2.c1=xx and t2.c30 = xx and t2.c2 != xx and t2.c9 between xx and xx
order by t2.c8
依据esro原则
1、我们需要先添加c1与c30 等值查询字段,我们再根据三星索引原则判断一下c1与c30的区分度,发现c1区分度很低。为性别字段,所以我们就可以先只加c30字段 idx(c30,
2、然后添加 排序字段 idx(c30,c8,
3、然后添加范围字段 同样去判断一下c9的区分度。如果还行则添加 idx(c30,c8,c9,
4、然后添加where 处的其它字段 idx(c30,c8,c9,c1,c2 添加c1,c2 不仅了为了索引覆盖,也可以实理icp(索引下推)
5、添加剩余的其它字段 select字段和join字段 idx(c30,c8,c9,c1,c2,c4,c40) c50为主键,二级索引叶子节点有,所以不需要添加。
所以依据esro原则和三星索引理论,最终的索引字段与顺序为idx(c30,c8,c9,c1,c2,c4,c40)
以上索引建立后, 就能实现复杂sql的快速查询了,对于此sql,也是一个三星索引
联合索引最佳实践 请记住esro原则