mysql - MySQL InnoDB 采用的执行计划效率不高

标签 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) where rnid in (select rec.rnid as record_id from ar_records rec where rnid in (6022342, 6256614, 5842714, 6302489)) 和 (5,6) 中的 fieldid

执行计划同计划2。

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

    解释 select * from ah_problems force index (rnid__fieldid) where rnid in (select rec.rnid as record_id from ar_records rec where rnid in (6022342, 6256614, 5842714, 6302489)) 和 fieldid in (5, 6) order by rnid, fieldid

计划 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'$$

最佳答案

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)

关于mysql - MySQL InnoDB 采用的执行计划效率不高,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16304875/

相关文章:

mysql - MariaDB/MySQL : Get checksum of InnoDB table

python - 更改 Django 默认模型设置

mysql - 有关将 MYSQL DB 及其数据从拉丁语转换为 UTF-8 的详细说明。那里有太多差异信息

PHP,从无限类别表中删除一个类别

mysql - 在 Django 中通过跨不同应用程序的 OneToOneField 重置数据库

mysql - 通过查询优化 MySQL COUNT/group : show only categories that have products associated

php - 获取开始日期和结束日期之间的数据

mysql - 在 UPDATE BETWEEN 查询中哪些行将被锁定?

mysql - 如何优化具有多个外连接到大表、group by 和 order by 子句的查询的执行计划?

mysql - SQL - 对子查询的调用太多