血的教训-记一次Mysql线上事故

2019-08-18  本文已影响0人  10xjzheng

昨天真是个悲伤的日子,背了来公司的第一个事故。
事故的根本原因是改动了一个查询条件,用了not in,导致无法命中排序索引,因为线上有个租户表数据有400w+,查询缓慢继而引发Mysql连接数撑爆了实例的CPU。

SQL:

SELECT
    `clue_csts`.`confirm_status`,
    `clue_csts`.`lose_type_id`,
    `b_broker_recommend`.`b_broker_recommendId`,
    `c`.`cst_name`,
    `b_broker_recommend`.`proj_id`,
    `b_broker_recommend`.`cst_id`,
    `c`.`mobile_tel`,
    `b_broker_recommend`.`last_report_time`,
    `b_broker_recommend`.`check_date`,
    `stage_id`,
    `is_delay`,
    `c`.`tel2`,
    `c`.`tel3`,
    `c`.`tel4`,
    `c`.`gender`
FROM
    `b_broker_recommend`
LEFT JOIN `b_broker_recommend_cst` `c` ON c.b_broker_recommend_cstId = b_broker_recommend.cst_id
LEFT JOIN `clue_csts` ON clue_csts.clue_cst_id = b_broker_recommend.clueId
WHERE
    `b_broker_recommend`.`regbroker_id` = '39ec911a-495a-3357-7e18-1a5c9e15279a'
AND b_broker_recommend.stage_id = '2'
AND (
    `c`.`mobile_tel` LIKE '%/+//Um9S5ErCikwXE4dXrWnuEg==/+//uuEySqVtmoCnOoPk9aV1fw==/+//GJg0MA+Q+xqQIknWJPJ8Nw==%'
    OR `c`.`tel2` LIKE '%/+//Um9S5ErCikwXE4dXrWnuEg==/+//uuEySqVtmoCnOoPk9aV1fw==/+//GJg0MA+Q+xqQIknWJPJ8Nw==%'
    OR `c`.`tel3` LIKE '%/+//Um9S5ErCikwXE4dXrWnuEg==/+//uuEySqVtmoCnOoPk9aV1fw==/+//GJg0MA+Q+xqQIknWJPJ8Nw==%'
    OR `c`.`tel4` LIKE '%/+//Um9S5ErCikwXE4dXrWnuEg==/+//uuEySqVtmoCnOoPk9aV1fw==/+//GJg0MA+Q+xqQIknWJPJ8Nw==%'
    OR `c`.`cst_name` LIKE '%13858616853%'
)
AND `b_broker_recommend`.`b_broker_recommendId` NOT IN (
    '39ec911d-1d24-6c6b-19cf-d4f90c34ec79',
    '39ed2864-8169-cec1-0391-51d8681500ec',
    '39ed7559-3889-6b8a-a977-835902f4dbbc',
    '39ede0e4-6baf-4e6e-11b8-789c285a8b0c',
    '39ede123-d7ab-6591-6a36-70791bf62a78',
    '39ee24b3-71f6-e4d1-a5b1-7bbfd16cd1de'
)
ORDER BY    `b_broker_recommend`.`check_date` DESC
LIMIT 20;

部分表名、字段名已被我替换掉。
索引是:
用来排序的符合索引:IX_regbroker_id,check_date (regbroker_id,check_date)。
explain:


image.png

从key那行可以看出排序索引是没有用到的,在check_date上对百万千万级别的数据进行排序之后再根据过滤条件来过滤,还有like和or,就死了,遇上not in比较多的这个sql要执行几十秒,并发连接一起来,死了,cpu就撑爆了,一大堆别的sql在等待执行。整个实例就挂了。


image.png
但我把not in里面的减少到两个,发现索引又用上了:
image.png
1s执行完。
3个以上就又不走了。

后来是又改成了连表进行过滤。这里就不贴SQL了。

结论:

  1. 慎用not in,特别是里面的枚举值多的情况,尽量不用,因为not in前面的字段是主键的缘故(多次测试),此条件会影响mysql的执行计划,其它表的字段虽然用了 like,or,但它依然会在索引上过滤并排序后进行过滤。
  2. 改变了sql的查询条件,特别是sql关联的表是大表,需要对多种场景进行explain,尽量防患于未然。
上一篇 下一篇

猜你喜欢

热点阅读