优化前语句:
SELECT
ifnull(s.mileage, -1) as mile,
s.fuel_hkm as val,
t.fhkm_rank as rank,
ifnull((select u.photo from app_user u where u.user_id = t.user_id limit 1),‘‘) as path,
case when s.car_id = ‘***********‘ then 0
else 1 end as self,
ifnull((
SELECT
case
when ifnull(u.nickname, ‘‘) != ‘‘ then u.nickname
when ifnull(u.username, ‘‘) != ‘‘ then u.username
when ifnull(u.customer_number, ‘‘) != ‘‘ then u.customer_number
else u.mobile END as uname
FROM
app_user u
WHERE
u.user_id = t.user_id
LIMIT 1
),‘‘) as manname,
ifnull((select case when ifnull(c.nickname,‘‘) != ‘‘ then c.nickname else REPLACE(c.lisence,SUBSTR(c.lisence,3,3),‘***‘) end from app_car c where c.car_id = t.car_id limit 1),‘‘) as carname
FROM
app_car_sum_7daily s,
app_rank_ka t
WHERE
t.date = ‘2016-07-12‘
and t.date = s.date
and t.car_id = s.car_id
and s.fuel_hkm > 0
and t.fhkm_rank > 0
ORDER BY rank,self,convert(manname using gbk) limit 10
优化后语句:
select * from (
select
ifnull(s.mileage, -1) as mile,
s.fuel_hkm as val,
t.fhkm_rank as rank,
ifnull(u.photo,‘‘) as path,
case when s.car_id = ‘***********‘ then 0
else 1 end as self,
case
when ifnull(u.nickname,‘‘) != ‘‘ then u.nickname
when ifnull(u.username,‘‘) != ‘‘ then u.username
when ifnull(u.customer_number,‘‘) != ‘‘ then u.customer_number
else ifnull(u.mobile,‘‘) end as manname,
case
when ifnull(c.nickname,‘‘) != ‘‘ then c.nickname
else ifnull(REPLACE(c.lisence,SUBSTR(c.lisence,3,3),‘***‘),‘‘) end as carname
from
app_rank_ka t
join app_car_sum_7daily s on (s.date = t.date and s.car_id = t.car_id and s.fuel_hkm > 0)
join app_user u on (u.user_id = t.user_id)
join app_car c on (c.car_id = t.car_id)
where t.date = ‘2016-07-12‘ and t.fhkm_rank > 0
ORDER BY rank limit 200) a order by rank,self,convert(manname using gbk) limit 10;
优化方法:
1、去子查询优化为join查询
2、子查询带limit 1的表链接键都为主键,所以不再需要limit 1
3、app_rank_ka(t)表,对别名为rank的字段和date字段建联合索引优化内层排序idx_date_rank
4、在不对sql业务进行判断及变更的情况下采用折中的办法,先根据rank limit 200条或者觉得合适的条数只要不是非常多,再进行业务排序
5、嵌套链接索引都很合理,该点无优化空间
优化原理:
1、该sql慢的主要原因在于order by 多个字段
2、mysql的order by只是利用sort_buffer做第一个字段的排序,后面的字段会在临时表中进行,并且计算量很大,根据optimizer_trace跟踪本来只有4万多条数据在排序三个字段之后扫描了14万次以上才完成
3、mysql的order by优化算法有两个,一种sort_buffer存的数据为(sort_key1,sort_key2,sort_key3,row_id)排序完成还需回表查询数据,另一种为(sort_key1,sort_key2,sort_key3,key1 value,key2 value,key3 value....)包含所有需求字段数据,在数据不超过阈值(max_length_for_sort_data)时默认都会采用第二种优化方式
3、上面sql采用的方式就利用了索引先优化从表里面取数据时的排序,限制了200条作为外层的排序数据,不过这也建立了一个临时表,鉴于数据长度不是很大,在数据量不多时效率影响有限,在外层利用三个字段进行排序不管是使用那一种sort算法查找数据都是很高效的
4、关联子查询在mysql查询优化器中处理都不是很如意,优化为join不仅避免优化器选择的问题也可以降低优化器的消耗时间
优化前后执行时间对比:
优化前:
| 1 | 8.94656525 |
| 2 | 8.77086475 |
优化后:
| 3 | 0.00527075 |
| 4 | 0.00513025 |
该sql有5种order by排序规则的查询,优化方式相同!!!!!,分别建date和这5个字段建立联合索引,可以删除现有的date单列所以,因为date字段为时间列不会做更新操作,即使该5个字段经常更新页之间数据移动也不会发生页分裂的情况,又因为mysql提供change_buffer为辅助索引提供内存缓冲,所以新建(5-1)个索引的维护成本完全可以接受
PS:如果业务上能修改排序条件,可以只使用子查询就行,效率更高,通常尽量只使用一个字段排序,在数据量大时多字段的排序耗时成倍增长
本文出自 “D调de默默” 博客,谢绝转载!
原文地址:http://xiaozhong991.blog.51cto.com/2354914/1826990