客户反映MYSQL某个报表前端查询很慢,前端展示需要10多秒,于是我方把监控到的SQL语句放在navicat工具执行查询:
SELECT
sum(num)
FROM
`user_coin_log` force index (ix_5)
WHERE
`create_time` >= 1584892800
AND `create_time` < 1584979200
AND `source` = 5
AND `uid` IN (
23,
24,
26,
29,
32,
33,
38,
42,
52,
59,
68,
69,
71,
72,
73,
79,
83,
85,
108,
111,
139,
229,
261,
280,
281,
283,
296,
298,
308,
401,
423,
490,
523,
650,
653,
776,
903,
913,
966,
997,
1030,
1381,
1704,
1809,
1922,
1943,
2011,
2037,
2146,
2459,
2757,
3055,
3087,
3098,
3280,
3315,
3427,
3701,
5132,
5625,
6646,
7628,
8040,
8464,
23304
)
AND `coin_id` = 1
LIMIT 1
执行时间需要5.4S,于是分析这个SQL语句,从语句的结构来看,where条件经过了时间create_time,uid等条件过滤,直觉应该不至于如此慢,而且该表上是有一个索引ix_5,同时覆盖了create_time,uid,source,coin_id,num等列的,但此SQL执行计划并没有走这个索引,而是走了一个单列索引UID不太合理,于是经过多方面排查与尝试,如果把这个UID条件去掉,或把UID里面的列表值大量减少,这个时候会走ix_5索引,甚至如果让uid in一个临时表(把所有的uid值建成1个临时表),这个时候也会走ix_5索引,执行时间会在0.8s内!
顺着这个方向继续排查,发现网上有不少类似的案例,其现象都是:in后面如有太多值,MYSQL会认为全面扫描会更有效率,看来这一点还是不够oracle的优化器智能啊!于是尝试采用强制索引的方案来解决,ORACLE有hint,MYSQL原来也可以,具体加上HINT
(force index (ix_5))后,语句变成:
SELECT
sum(num)
FROM
`user_coin_log` force index (ix_5)
WHERE
`create_time` >= 1584892800
AND `create_time` < 1584979200
AND `source` = 5
AND `uid` IN (
23,
24,
26,
29,
32,
33,
38,
42,
52,
59,
68,
69,
71,
72,
73,
79,
83,
85,
108,
111,
139,
229,
261,
280,
281,
283,
296,
298,
308,
401,
423,
490,
523,
650,
653,
776,
903,
913,
966,
997,
1030,
1381,
1704,
1809,
1922,
1943,
2011,
2037,
2146,
2459,
2757,
3055,
3087,
3098,
3280,
3315,
3427,
3701,
5132,
5625,
6646,
7628,
8040,
8464,
23304
)
AND `coin_id` = 1
LIMIT 1
再次执行:
加上HINT后,SQL效率得到了10倍以上的提升,由原来的5.4S下降到0.3S,性能提升效果显著!
后续思考:这个方案虽然临时解决了问题,优化效果也非常显著,但MYSQL5.7照理没那么缺陷吧?后面再排查一下是否可以通过修改某些优化器相关参数来达到同等优化效果!
发表评论 取消回复