共计 3255 个字符,预计需要花费 9 分钟才能阅读完成。
简介:菜鸟供应链金融慢 sql 治理曾经有一段时间,本人负责的利用继续很长时间没有慢 sql 告警,现阶段在推动组内其余成员治理利用慢 sql。这里把治理过程中的一些实际拿进去分享下。
作者 | 如期
起源 | 阿里技术公众号
菜鸟供应链金融慢 sql 治理曾经有一段时间,本人负责的利用继续很长时间没有慢 sql 告警,现阶段在推动组内其余成员治理利用慢 sql。这里把治理过程中的一些实际拿进去分享下。
一 全表扫描
1 案例
SELECT count(*) AS tmp_count FROM (SELECT * FROM `XXX_rules` WHERE 1 = 1 ORDER BY gmt_create DESC) a
2 溯源
在分页查问治理的文章里曾经介绍过咱们零碎旧的分页查问逻辑,下面的查问 sql 显著就是分页查问获取总记录数,通过 XXX_rules 表的分页查问接口溯源,找到发动调用的页面是咱们小二后盾的一个操作商家准入的页面,页面关上后间接调用分页查问接口,除了分页参数,不传入其余任何查问参数,导致扫描全表。
3 剖析
灵魂拷问:为什么要扫描全表?全表数据展现到页面,花里胡哨的数据有用吗?
调研:和常常应用这个页面的经营聊后理解到,关上页面查问出的全表数据对经营是没有用的,他们基本不看这些数据。经营的操作习惯是拿到商家 id,在页面查问框中输出商家 id,查到商家数据后进行操作。
4 解决方案
由此优化计划就很清朗了:关上页面时不间接查问全量数据,等经营输出商家 id 后,将商家 id 作为参数进行查问。XXX_rules 表中,商家 id 这一罕用查问条件设置为索引,再联合分页查问优化,全表扫描慢 sql 得以解决。
优化后的小二后盾页面如下:
关上页面时未查问任何数据,查问条件商家账户为必填项。
优化后的 sql 为:
SELECT count(*) AS tmp_count FROM (SELECT * FROM `xxx_rules` WHERE 1 = 1 AND `rule_value` = '2928597xxx') a
执行 EXPLAIN 失去后果如下:
能够看到命中了索引,扫描行数为 3,查问速度明显提高。
5 思考
扫描全表治理简略来说就是退出查问条件,命中索引,去除全表扫描查问,尽管有些粗犷,但并不是没有情理。理论业务场景中,很少有要扫描全表获取全副数据的状况,限度调用上游必须传入查问条件,且该查问条件能命中索引,能很大水平上防止慢 sql。
另外,再引申下,XXX_rules 初始的用意是准入表,记录金融货主维度的准入状况,最多也就几千条数据,然而很多共事将这张表了解为规定表,写入很多业务相干规定,导致这个表收缩到一百多万条数据,表不 clean 了。这就波及到数据表的设计应用,明确表的应用标准,不乱写入数据,能给前期保护带来很大的便当。
二 索引凌乱
1 示例
2 剖析
除了工夫、操作人字段,XXX_rules 表就 rule_name、rule_value、status、product_code 四个字段,表的索引对这四个字段做各种排列组合。存在如下问题:
1、rule_name 离散度不高,放在索引首位不适合;
2、前三个索引重合度很高;
显然是对索引的命中规定不够理解。XXX_rules 表很多业务有定时工作对其写入删除,索引多、凌乱,对性能有很大的影响。
高性能的索引有哪些,再来回顾下:
1、独立的列:索引列不能是表达式的一部分;
2、抉择区分度高的列作为索引;
3、抉择适合的索引列程序:将选择性高的索引列放在最前列;
4、笼罩索引:查问的列均在索引中,不须要回查聚簇索引;
5、应用索引扫描来做排序;
6、在恪守最左前缀的准则下,尽量扩大索引,而不是创立索引。
凡是记得第 3 和 6 规定,也不至于把索引建成这样。
3 治理
对索引进行整合如下:
零碎中有很多工作拉取整个产品下的准入记录,而后进行解决,所以将区分度较高的 product_code 放在索引首位,而后增加 rule_name、status 字段到索引里,进一步过滤数据,缩小扫描行数,防止慢 sql。针对罕用的 rule_value 查问条件,能够命中 UK,因而不必独自建设索引。
三 非必要排序
1 问题形容
很多业务逻辑中,须要拉取满足某个条件的记录列表,查问的 sql 语句带有 order by,记录比拟多的状况,排序代价往往很大,然而查问进去的记录是否有序对业务逻辑没有影响,比方分页治理里探讨的 count 语句,只须要统计条数,order by 对条数没有影响,再比方查出记录列表后,不依赖记录的程序遍历列表解决数据,这时候 order by 多此一举。
2 解决方案
查问 sql 无 limit 语句,且业务解决逻辑不依赖于 order by 后列表记录的程序,则去除查问 sql 中的 order by 语句。
四 粗粒度查问
1 问题形容
业务中有很多定时工作,扫描某个表中某个产品下所有数据,对数据进行解决,比方:
SELECT * FROM XXX_rules
WHERE rule_name = 'apf_distributors'
AND status = '00'
AND product_code = 'ADVANCE'
三个查问条件都是区分度不高的列,查出的数据有 27W 条,加索引意义也不大。
2 剖析
理论业务量没那么大,顶多几千条数据,表里的数据是从上游同步过去的,最好的方法是让上游精简数据,然而因为业务太长远,找上游的人保护难度太大,因而只能想其余的方法。
这个定时工作目标是拉出 XXX_rules 表的某些产品下的数据,和另一张表数据比照,更新有差别的数据。每天凌晨解决,对时效性没有很高的要求,因而,能不能转移工作解决的中央,不在本利用机器上实时处理那么多条数据?
3 解决方案
数据是离线工作 odps 同步过去的,首先想到的就是 dataWork 数据处理平台。
建设数据比照工作,将定时工作做的数据比照逻辑放到 dataWork 上用 sql 实现,每天差别数据最多几百条,且后果集含有区分度很高的列,将差别数据写入 odps 表,再将数据回流到 idb。
新建定时工作,通过回流回来的差别数据中区分度高的列作为查问条件查问 XXX_rules,更新 XXX_rules,解决了慢 sql 问题。
这个办法的前提是对数据实效性要求不高,且离线产出的后果集很小。
五 OR 导致索引生效
1 案例
SELECT count(*)
FROM XXX_level_report
WHERE 1 = 1
AND EXISTS (
SELECT 1
FROM XXX_white_list t
WHERE (t.biz_id = customer_id
OR customer_id LIKE CONCAT(t.biz_id, '@%'))
AND t.status = 1
AND (t.start_time <= CURRENT_TIME
OR t.start_time IS NULL)
AND (t.end_time >= CURRENT_TIME
OR t.end_time IS NULL)
AND t.biz_type = 'GOODS_CONTROL_BLACKLIST'
)
2 剖析
explain 上述查问语句,失去后果如下:
XXX_white_list 表有将 biz_id 作为索引,这里查问 XXX_white_list 表有传入 biz_id 作为查问条件,为啥 explain 后果里 type 为 ALL,即扫描全表?索引生效了?索引生效有哪些状况?
索引生效场景
1、OR 查问左右有未命中索引的;
2、复合索引不满足最左匹配准则;
3、Like 以 % 结尾;
4、须要类型转换;
5、where 中索引列有运算;
6、where 中索引列应用了函数;
7、如果 mysql 感觉全表扫描更快时(数据少时)
上述查问语句第 8 行,customer_id 为 XXX_level_report 表字段,未命中 XXX_white_list 表索引,导致索引生效。
3 解决方案
这个语句用 condition、枚举、join 花里胡哨的代码拼接起来的,改起来好麻烦,而且看起来“OR customer_id LIKE CONCAT(t.biz_id, ‘@%’)”这句不能间接删掉。最初重构了该局部的查问语句,去除 or 查问,解决了慢 sql。
原文链接
本文为阿里云原创内容,未经容许不得转载。