MySQL InnoDB采取的效率不高的执行计划

时间:2013-04-30 16:37:09

标签: mysql innodb sql-optimization

我无法使用MySQL InnoDB优化器优化请求。 以下查询(查询1)有效运行:

explain select * from ah_problems
where rnid in (6022342, 6256614, 5842714, 6302489)
and fieldid in (5,6);

和计划(计划1)如下:

id select_type table       type  possible_keys                   key           key_len ref rows Extra
=  ======      =========== ===== =============================== ============= ======= === ==== =====
1  SIMPLE      ah_problems range CONSTRAINTFIELDID,RNID__FIELDID RNID__FIELDID 8           33   Using where

到目前为止,非常好。

下面稍加修改的查询(查询2)将采取灾难性的执行计划:

explain select * from ah_problems
where rnid in (select rec.rnid as record_id from ar_records rec where rnid in (6022342, 6256614, 5842714, 6302489))
and fieldid in (5, 6)

结果是一样的,但计划(计划2)现在正在这样做:

id select_type        table       type            possible_keys      key      key_len ref  rows     Extra
=  ======             =========== =====           ================== ======== ======= ==== =======  =====
1  PRIMARY            ah_problems ALL             CONSTRAINTFIELDID                        36177754 Using where
2  DEPENDENT SUBQUERY rec         unique_subquery PRIMARY            PRIMARY  4       func 1        Using index; Using where

如果你想知道,那个新的子查询...

select rec.rnid as record_id from ar_records rec where rnid in (6022342, 6256614, 5842714, 6302489)

...只是返回在查询1中硬编码的四行:

6022342
6256614
5842714
6302489

所以查询(1)和(2)是等价的。

猜猜是什么,我需要查询2,而不是一个。我希望查询2与查询1一样高效。我尝试了以下内容:

  1. 查询3:将FORCE INDEX(RNID_FIELDID)添加到查询2. MySQL只是忽略它。

    解释select * from ah_problems force index(rnid__fieldid) 其中rnid in(从ar_records rec中选择rec.rnid作为record_id,其中rnid in(6022342,6256614,584214,6302489)) and fieldid in(5,6)

  2. 执行计划与计划2相同。

    1. 查询4:向查询添加ORDER BY RNID, FIELDID 3.我在其他一些问题上看到这可能会欺骗优化器。它没有帮助。

      解释select * from ah_problems force index(rnid__fieldid) 其中rnid in(从ar_records rec中选择rec.rnid作为record_id,其中rnid in(6022342,6256614,584214,6302489)) 和(5,6)中的fieldid由rnid,fieldid

    2. 排序

      计划4现在正在使用索引,但行数仍然是灾难性的:

      id select_type        table       type            possible_keys      key           key_len ref  rows      Extra
      =  ======             =========== =====           ================== ========      ======= ==== =======   =====
      1  PRIMARY            ah_problems index                              RNID__FIELDID 8             36179307 Using where
      2  DEPENDENT SUBQUERY rec         unique_subquery PRIMARY            PRIMARY       4       func  1        Using index; Using where
      

      如果这有帮助,这就是我的ah_problems表的定义。遗憾的是,我无法更改表格的定义。有什么办法可以让MySQL优化器使用计划1来攻击查询2中的表ah_problems吗?

      CREATE TABLE `ah_problems` (
        `ID` int(11) NOT NULL AUTO_INCREMENT COMMENT 'Identifier for update statements',
        `RNID` int(11) NOT NULL COMMENT 'Record number',
        `FIELDID` int(11) NOT NULL COMMENT 'Which field is value in',
        `VALUE` varchar(255) NOT NULL COMMENT 'The value the field got on MODIFIED_DATE',
        `PREVIOUSID` int(11) DEFAULT NULL COMMENT 'Reference to previous value',
        `MODIFIED_DATE` datetime NOT NULL COMMENT 'When was it changed',
        `MODIFIED_GROUPID` int(11) DEFAULT NULL COMMENT 'In what group did modified_userid change it',
        `MODIFIED_USERID` int(11) NOT NULL COMMENT 'Who changed it',
        PRIMARY KEY (`ID`),
        KEY `CONSTRAINTFIELDID` (`FIELDID`),
        KEY `CONSTRAINTMODIFIED_GROUPID` (`MODIFIED_GROUPID`),
        KEY `CONSTRAINTMODIFIED_USERID` (`MODIFIED_USERID`),
        KEY `CONSTRAINTPREVIOUSID` (`PREVIOUSID`),
        KEY `RNID__FIELDID` (`RNID`,`FIELDID`),
        CONSTRAINT `HPRB_FIELD` FOREIGN KEY (`FIELDID`) REFERENCES `ad_fields` (`ID`),
        CONSTRAINT `HPRB_MODIFIED_GROUP` FOREIGN KEY (`MODIFIED_GROUPID`) REFERENCES `ap_groups` (`ID`),
        CONSTRAINT `HPRB_MODIFIED_USER` FOREIGN KEY (`MODIFIED_USERID`) REFERENCES `ap_users` (`ID`),
        CONSTRAINT `HPRB_PREVIOUS` FOREIGN KEY (`PREVIOUSID`) REFERENCES `ah_problems` (`ID`) ON DELETE CASCADE,
        CONSTRAINT `HPRB_RN` FOREIGN KEY (`RNID`) REFERENCES `ar_records` (`RNID`)
      ) ENGINE=InnoDB AUTO_INCREMENT=72305308 DEFAULT CHARSET=utf8 COMMENT='PTR history'$$
      

2 个答案:

答案 0 :(得分:1)

MySQL无法优化IN子查询(仅执行一次),它总是在循环中为主查询中的每条记录执行。

将其替换为连接:

SELECT ahp.*
FROM   ar_records ar
JOIN   ah_problems ahp
ON     ahp.rnid = ar.rnid
       AND ahp.fieldId IN (5, 6)
WHERE  ar.rnid IN (6022342, 6256614, 5842714, 6302489)

答案 1 :(得分:1)

我相信MySQL需要在子查询之前解析WHERE fieldid IN (5, 6)。 尝试添加以下索引:

ALTER TABLE ah_problems ADD INDEX FIELDID__RNID (FIELDID, RNID);

如果没有帮助,请将您的查询重写为JOIN

SELECT *
FROM ah_problems
JOIN ar_records USING (rnid)
WHERE rnid in (6022342, 6256614, 5842714, 6302489)
AND fieldid in (5, 6)