点击上方SQL数据库开发,关注获取SQL视频教程
SQL专栏
SQL数据库基础知识汇总SQL数据库高级知识汇总作者:风过无痕-唐
www.cnblogs.com/tangyanbo/p/6378741.html
奇怪的慢sql
我们先来看2条sql
第一条:
select * from acct_trans_log WHERE acct_id = 1000000000009000757
order by create_time desc limit 0,10
第二条:
select * from acct_trans_log WHERE acct_id = 1000000000009003061
order by create_time desc limit 0,10
表的索引及数据总情况: 索引:acct_id,create_time 分别是单列索引,数据库总数据为500w。 通过 acct_id 过滤出来的结果集在 1w 条左右。 查询结果:第一条要5.018s,第二条0.016s 为什么会是这样的结果呢?第一,acct_id和create_time都有索引,不应该出现5s查询时间这么慢啊 那么先来看执行计划 第一条sql执行计划:
select * from acct_trans_log force index(idx_acct_id)
WHERE acct_id = 1000000000009000757
order by create_time desc limit 0,10
耗时: 0.057s 可以看出改情况用idx_acct_id索引是比较快的,那么是不是这样就可以了呢,排序未用上索引,始终是有隐患的。
联合索引让where和排序字段同时用上索引 我们来看下一条sql:
select * from acct_trans_log force index(idx_acct_id)
WHERE acct_id = 3095
order by create_time desc limit 0,10
耗时: 1.999s 执行计划:
alter table acct_trans_log add index idx_acct_id_create_time(acct_id,create_time)
然后执行sql:
select * from acct_trans_log WHERE acct_id = 3095
order by create_time desc limit 0,10
耗时: 0.016s
联合索引让where条件字段和排序字段都用上了索引,问题解决了!
联合索引使用的原理
但是为什么能解决这个问题呢,这时大家可能就会记住一个死理,就是联合索引可以解决where过滤和排序的问题,也不去了解其原理,这样是不对的,因为当情况发生变化,就懵逼了,
下面我们再看一个sql:
select * from acct_trans_log force index(idx_acct_id_create_time)
WHERE acct_id in(3095,1000000000009000757)
order by create_time desc limit 0,10
耗时: 1.391s 索引还是用idx_acct_id_create_time,时间居然慢下来了。 执行计划是:
select * from acct_trans_log
ORDER BY create_time limit 0,100
耗时: 0.029s 执行计划为:
select * from acct_trans_log
WHERE acct_id = 3095
order by create_time desc limit 0,10
使用组合索引: idx_acct_id_create_time。 这个时候,因为acct_id是联合索引的前缀,因此可以很快实行检索, 如果sql是
select * from acct_trans_log WHERE acct_id = 3095
出来的数据是按如下逻辑排序的
3095+time13095+time23095+time3默认是升序的,也就是说,次sql相当于
select * from acct_trans_log
WHERE acct_id = 3095
order by create_time
他们是等效的。 如果我们把条件换成order by create_time desc limit 0,10呢? 这时候,应该从idx_acct_id_create_time树右边叶子节点倒序遍历,取出前10条即可 因为数据的前缀都是3095,后缀是时间升序。那么我们倒序遍历出的数据,刚好满足 order by create_time desc。 因此也无需排序。 那么语句:
select * from acct_trans_log force index(idx_acct_id_create_time)
WHERE acct_id in(3095,1000000000009000757)
order by create_time desc limit 0,10
为什么排序无法用索引呢? 我们先分析下索引的排序规则, 已知:id1 查询结果集排序如下: id1+time1 id1+time2 id1+time3 id2+time1 id2+time2 id2+time3 索引出来的默认排序是这样的,id是有序的,时间是无序的,因为有2个id,优先按id排序,时间就是乱的了, 这样排序将会用filesort,这就是慢的原因,也是排序没有用到索引的原因。
查询计划使用以及使用说明
table:显示这一行数据是关于哪张表的 type:显示使用了何种类型,从最好到最差的连接类型为const,eq_ref,ref,range,index,all possible_keys:显示可能应用在这张表中的索引。如果为空,没有可能的索引 key:实际使用的索引,如果为null,则没有使用索引。 key_len:使用的索引的长度。在不损失精确性的情况下,长度越短越好 ref:显示索引的哪一列被使用了,如果可能的话,是一个常数 rows:mysql认为必须检查的用来返回请求数据的行数——End——后台回复关键字:1024,获取一份精心整理的技术干货后台回复关键字:进群,带你进入高手如云的交流群。推荐阅读
MySQL主从复制配置详解
神奇的 SQL,GROUP BY 真扎心,原来是这样!
为什么阿里巴巴禁止使用存储过程?
同事给我埋了个坑:INSERT INTO SELECT把生产服务器炸了
阿里规定超过3张表,禁止JOIN,为何?
这是一个能学到技术的公众号,欢迎关注