关于sql:慢sql治理经典案例分享

54次阅读

共计 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。

原文链接
本文为阿里云原创内容,未经容许不得转载。

正文完
 0