【问题标题】:MySQL examining too many rows when WHERE clause has more than one IN当 WHERE 子句有多个 IN 时,MySQL 检查太多行
【发布时间】:2013-05-20 15:26:47
【问题描述】:

我目前使用的是 MySQL 5.6.10。

我的实际查询更复杂,但这里有一个重现问题的简单方法。我知道下面的查询是没用的(select id from x where id in (select id from x...)),但它证明了我的观点。

我创建了这张表:

CREATE  TABLE test (
  id INT NOT NULL AUTO_INCREMENT ,
  PRIMARY KEY (id));

然后运行此命令 5 次 - 它在表中创建了 50 行:

INSERT INTO test (id) VALUES(null),(null),(null),(null),(null),(null),(null),(null),(null),(null);

然后运行这个解释:

EXPLAIN SELECT id FROM test WHERE 
       id in (SELECT id FROM test WHERE id < 5);

得到了这个:

这对我来说很有意义。但是,如果我用另一个 IN 在 WHERE 子句中添加一个 OR,如下所示:

EXPLAIN SELECT id FROM test WHERE 
       id IN (SELECT id FROM test WHERE id < 5)
    OR id IN (SELECT id FROM test WHERE id > 45);

突然 MySQL 正在查看所有 50 行:

我知道查询可以重写为SELECT id FROM test WHERE id &lt; 5 OR id &gt; 45 或UNION 等,同样,这不是重点。关键是 MySQL 正在检查太多行。

如果我在第一个查询中运行 FLUSH STATUS / SHOW STATUS LIKE "Handler%",这就是我得到的结果:

Handler_read_key 5
Handler_external_lock 4
Handler_read_next 4
Handler_read_first 1

但如果我对第二个查询这样做,我会得到:

Handler_read_key 99
Handler_write 9
Handler_external_lock 6
Handler_read_next 59
Handler_read_first 2

为什么会有很大的不同?我想知道它是否是优化器,如果是,是否可以在查询中包含一些选项来阻止这种“优化”?这对我正在开发的查询具有真正的实际意义。 MySQL 不是只检查几百行,而是检查 120,000 行。

【问题讨论】:

  • 因为你正在做 ins 颠簸,这意味着你没有从 join 中受益,但正在遭受指数级灾难的愤怒......如前所述,使用 ins !

标签: mysql where explain


【解决方案1】:

一般来说,RDBMS 不能像优化正确的表连接一样优化子查询。如Rewriting Subqueries as Joins 中所述(已添加重点):

有时,除了使用子查询之外,还有其他方法可以测试一组值中的成员资格。此外,在某些情况下,不仅可以在没有子查询的情况下重写查询,而且使用其中一些技术比使用子查询更有效强>。其中之一是IN() 构造:

例如,这个查询:

SELECT * FROM t1 WHERE id IN (SELECT id FROM t2);

可以改写为:

SELECT DISTINCT t1.* FROM t1, t2 WHERE t1.id=t2.id;

在您的抽象情况下(即忽略现实中对该查询所做的其他明显改进):

SELECT DISTINCT t1.*
FROM   test t1
  JOIN test t2 USING (id)
  JOIN test t3 USING (id)
WHERE  t2.id < 5
    OR t3.id > 45;

执行计划是:

+----+-------------+--------+--------+------------- --+---------+---------+------+------+- ----------------------------------------------------------+ |身份证 |选择类型 |表 |类型 |可能键 |关键 | KEY_LEN |参考 |行 |额外 | +----+-------------+--------+--------+------------- --+---------+---------+------+------+- ----------------------------------------------------------+ | 1 |简单 | t1 |范围 |初级 |初级 | 4 | (空) | 9 |使用哪里;使用索引;使用临时 | | 1 |简单 | t2 | eq_ref |初级 |初级 | 4 | db_2_129b4.t1.id | 1 |使用索引;区别 | | 1 |简单 | t3 | eq_ref |初级 |初级 | 4 | db_2_129b4.t1.id | 1 |使用索引;区别 | +----+-------------+--------+--------+------------- --+---------+---------+------+------+- ----------------------------------------------------------+

sqlfiddle 上查看。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-08-14
    • 1970-01-01
    • 1970-01-01
    • 2012-01-28
    • 1970-01-01
    • 2019-06-24
    • 1970-01-01
    相关资源
    最近更新 更多