0
点赞
收藏
分享

微信扫一扫

SQL优化:为什么我的SQL变慢了?

小龟老师 2021-09-21 阅读 27

客户问:有张表的索引做了调整后,为什么这个SQL要执行3个小时?

SELECT
 NULL,
 M.SENDDATE,
 T.RELISN,
 ...
FROM
 xxx M, xxx T
WHERE T.RELISN = M.ISN
AND M.SUBTYPE = '1'
AND M.ZONENO NOT IN (SELECT D.DICTCODE FROM T_CDRI_DICT D WHERE D.DICTTYPE = 'CMBZONENO')
AND M.SDSTATUS IN ('007','010')
AND M.INKFLAG = '1'
AND M.SENDDATE IN (SELECT DATE_ADD(CTL.FILEDATE, INTERVAL 1 DAY)
          FROM T_FEEDBACK_WFFDCTL_ICBC CTL WHERE CTL.FILETYPE IN ('A', 'B', 'C', 'D', 'E', 'F'))
AND EXISTS(
  SELECT CODE FROM T_CPRI_NTHPADIC
    WHERE NAME='BOPTCFLAG' AND CODE='NOV_BOP'
  AND (ZONENO='00000' OR ZONENO=M.ZONENO)
    AND INFORMATION='1'
  AND DATE_SUB(STR_TO_DATE(M.SENDDATE, '%Y-%m-%D'),INTERVAL 1 DAY) BETWEEN ACTDATE AND DEADLINE);

调整前:

PRIMARY KEY (`ISN`,`ZONENO`),
  UNIQUE KEY `IDX_T_DTET_BOPMAIN_RPTNO` (`SUBTYPE`,`RPTNO`,`ZONENO`),    
  KEY `IDX_T_DTET_BOPMAIN_REFNO` (`ZONENO`,`SUBTYPE`,`REFNO`),
  KEY `IDX_T_DTET_BOPMAIN_INKFLAG` (`INKFLAG`,`BOPCODE`) USING BTREE,
  
  KEY `IDX_T_DTET_BOPMAIN_ZONENO` (`ZONENO`,`SUBTYPE`,`SETDATE`,`BRNO`,`SDSTATUS`,`PINBRNO`,`DPBRNO`,`UPDUSERID`),
  KEY `IDX_T_DTET_BOPMAIN_SDSTATUS` (`SDSTATUS`,`SENDDATE`,`BOPCODE`) USING BTREE,
  KEY `IDX_T_DTET_BOPMAIN_SETDATE` (`SETDATE`,`SUBTYPE`)

调整后:

 PRIMARY KEY (`ISN`,`ZONENO`),
  UNIQUE KEY `IDX_T_DTET_BOPMAIN_RPTNO` (`SUBTYPE`,`RPTNO`,`ZONENO`),
  KEY `IDX_T_DTET_BOPMAIN_REFNO` (`ZONENO`,`SUBTYPE`,`REFNO`),
  KEY `IDX_T_DTET_BOPMAIN_INKFLAG` (`INKFLAG`,`BOPCODE`) USING BTREE,
  
  KEY `IDX_T_DTET_BOPMAIN_SETDATE` (`SETDATE`,`SUBTYPE`,`ZONENO`,`BRNO`,`SDSTATUS`,`PINBRNO`,`DPBRNO`,`UPDUSERID`),
  KEY `IDX_T_DTET_BOPMAIN_UPDDATE` (`UPDDATE`,`SUBTYPE`,`ZONENO`),
  KEY `IDX_T_DTET_BOPMAIN_SDSTATUS` (`BOPCODE`,`SDSTATUS`,`SENDDATE`,`BATCHNO`)

执行计划:

+----+--------------------+-----------------+-------------------------------------------------------------------------------------------------------------------+------+-------------------------------------------------------------+-----------------------------+---------+-------------------+----------+----------+----------------------------------------------------+
| id | select_type        | table           | partitions                                                                                                        | type | possible_keys                                               | key                         | key_len | ref               | rows     | filtered | Extra                                              |
+----+--------------------+-----------------+-------------------------------------------------------------------------------------------------------------------+------+-------------------------------------------------------------+-----------------------------+---------+-------------------+----------+----------+----------------------------------------------------+
|  1 | PRIMARY            | <subquery3>     | NULL                                                                                                              | ALL  | NULL                                                        | NULL                        | NULL    | NULL              |     NULL |   100.00 | NULL                                               |
|  1 | PRIMARY            | M               | PT099,PT002,PT004,PT006,PT008,PT010,PT012,PT014,PT016,PT018,PT020,PT022,PT024,PT026,PT028,PT030,PT032,PT098,PT040 | ALL  | PRIMARY,IDX_T_DTET_BOPMAIN_RPTNO,IDX_T_DTET_BOPMAIN_INKFLAG | NULL                        | NULL    | NULL              | 50908218 |     0.50 | Using where; Using join buffer (Block Nested Loop) |
|  1 | PRIMARY            | T               | PT099,PT002,PT004,PT006,PT008,PT010,PT012,PT014,PT016,PT018,PT020,PT022,PT024,PT026,PT028,PT030,PT032,PT098,PT040 | ref  | IDX_T_DTET_BOP01_DTL_RELISN                                 | IDX_T_DTET_BOP01_DTL_RELISN | 9       | ioma.M.ISN        |        1 |   100.00 | Using index condition                              |
|  3 | MATERIALIZED       | CTL             | NULL                                                                                                              | ALL  | WFFDBAK                                                     | NULL                        | NULL    | NULL              |       15 |    40.00 | Using where                                        |
|  4 | DEPENDENT SUBQUERY | T_CPRI_NTHPADIC | NULL                                                                                                              | ref  | IDX_T_CPRI_NTHPADIC_ZONENO,IDX_T_CPRI_NTHPADIC_NAME         | IDX_T_CPRI_NTHPADIC_NAME    | 529     | const,const,const |      356 |     4.44 | Using where; Using index                           |
|  2 | DEPENDENT SUBQUERY | D               | NULL                                                                                                              | ref  | PRIMARY                                                     | PRIMARY                     | 92      | const             |        3 |   100.00 | Using where; Using index                           |
+----+--------------------+-----------------+-------------------------------------------------------------------------------------------------------------------+------+-------------------------------------------------------------+-----------------------------+---------+-------------------+----------+----------+----------------------------------------------------+

以上就是客户提供的全部信息,“什么?你还想问调整前的执行计划?”,“抱歉,没有了”。

分析过程

首先别看这个 SQL 它又长又宽,实际上通过执行计划,我们只需要关注 M 表,只有它扫描行数超多,所以我们只需要分析 where 条件中与 M 表相关的条件即可,子查询可以忽略(因为这些子查询与 M 表无关):

AND M.SUBTYPE = '1'
AND M.ZONENO NOT IN (...)
AND M.SDSTATUS IN ('007','010')
AND M.INKFLAG = '1'
AND M.SENDDATE IN (...)

还好我们知道背景是修改了索引导致了慢,所以可以判断修改前应该可以走这个索引:

KEY `IDX_T_DTET_BOPMAIN_SDSTATUS` (`SDSTATUS`,`SENDDATE`,`BOPCODE`) USING BTREE

修改索引后,这个索引变成:

KEY `IDX_T_DTET_BOPMAIN_SDSTATUS` (`BOPCODE`,`SDSTATUS`,`SENDDATE`,`BATCHNO`)

根据最左前缀原则,where 条件中并没有 BOPCODE 字段,所以无法使用这个索引。而执行计划中优化器可能选择的索引:

PRIMARY,IDX_T_DTET_BOPMAIN_RPTNO,IDX_T_DTET_BOPMAIN_INKFLAG

从字段命名来看,选择度都非常低,优化器最终是不会选择这些索引。

总结

最左前缀原则,如果 where 条件中的字段不在组合索引的第一位也就是最左边,那么是无法使用这个索引的;
字段的选择度或者说基数,太小的话建索引也是没用的。
当然这些知识我估计作为DBA都清楚,但是灵活运用到实际的优化分析中可能就难了,注意过滤无效的信息,保持清晰的思路!

举报

相关推荐

0 条评论