【问题标题】:MySQL performs a slow query with a boolean conditionMySQL 使用布尔条件执行慢速查询
【发布时间】:2016-05-16 23:24:16
【问题描述】:

表格示例:

       id       |      source     | removed
17D30437329A9B9 |                 |   0
M851X0LG81045F  | 17D30437329A9B9 |   0
QQG1RU1M8E5JHO  |                 |   0
QDVHFNFKF0Z80W  | 17D30437329A9B9 |   0
8BEFSFGUPBXJHV  |                 |   0

当我查询时:

SELECT  *
FROM    `uploads`
WHERE   (id = '17D30437329A9B9 ' OR `source` = '17D30437329A9B9 ')
AND     removed = 0

查询耗时约 25 秒(我有约 1700 万行)。

但是当我运行这个时:

SELECT  *
FROM    `uploads`
WHERE   (id = '17D30437329A9B9 ' OR `source` = '17D30437329A9B9 ')

或者这个:

SELECT  *
FROM    `uploads`
WHERE   (`id` = '17D30437329A9B9 ')
AND     removed = 0

或者说:

SELECT  *
FROM    `uploads`
WHERE   (`source` = '17D30437329A9B9 ')
AND     removed = 0

查询运行速度很快。

为什么第一个查询运行很慢,我该如何解决?

编辑: 来自EXPLAIN SELECT * FROM uploads WHERE (id = '17D30437329A9B9 ' OR source = '17D30437329A9B9 ') AND removed = 0;的结果:

+----+-------------+---------+------+-------------------------------------------------------+---------+---------+-------+---------+-------------+
| id | select_type | table   | type | possible_keys                                         | key     | key_len | ref   | rows    | Extra       |
+----+-------------+---------+------+-------------------------------------------------------+---------+---------+-------+---------+-------------+
|  1 | SIMPLE      | uploads | ref  | PRIMARY,removed,source,idx_member_selectFiles,id,id_2 | removed | 1       | const | 8829521 | Using where |
+----+-------------+---------+------+-------------------------------------------------------+---------+---------+-------+---------+-------------+

【问题讨论】:

    标签: mysql sql


    【解决方案1】:

    好像没有复合索引。

    运行并重试

    创建复合索引

    ALTER TABLE `uploads`
    ADD KEY (`id`,`source`,`removed`);
    

    如果有效,请告诉我。

    【讨论】:

    • 不,仍然需要大约 25 秒。此外,在我已经有一个类似的索引之前,还有另一个名为“服务器”的列 - (id,source,server,removed)。也许它以某种方式打扰它?
    • 请从以下位置发布输出:EXPLAIN SELECT * FROM uploads WHERE (id = '17D30437329A9B9 ' OR source = '17D30437329A9B9 ') AND removed = 0;
    • 他们没有使用最好的索引。让我们看看为什么。请同时添加以下输出:SHOW CREATE TABLE uploads;和 SELECT * FROM uploads PROCEDURE ANALYSE(); - 谢谢
    • 我需要在 1700 万行的生产环境中执行此操作,还是可以在 localhost 的多行中执行此操作?
    • 他们只看几 k 行,但首先你可以从 localhost 发布它
    【解决方案2】:

    B-Tree 索引不适合基数低的列。在您的情况下,MySQL 选择列 removed 的索引非常糟糕,因为只存在两个不同的值。

    我怀疑在removed 上永远不会看到索引的好处。删除removed上的索引。

    位图索引而不是 B 树索引会很好。据我所知 MySQL 不支持位图索引。

    此外,在这种情况下,(id, source) 上的索引会有所帮助。

    【讨论】:

      【解决方案3】:

      具有OR 条件的查询的 MySQL 执行计划有时不是最佳的。

      我建议您重新编写查询以组合来自两个单独查询的结果。

      SELECT u1.*
        FROM `uploads` u1
       WHERE u1.id      = '17D30437329A9B9 '
         AND u1.removed = 0
      
       UNION ALL
      
      SELECT u2.*
        FROM `uploads` u2
       WHERE u2.source  = '17D30437329A9B9 '
         AND u2.removed = 0
         AND NOT (u2.id <=> '17D30437329A9B9 ')
      

      每个 SELECT 都能有效利用最合适的索引。

      第一个 SELECT 可以使用前导列为 id 的索引。第二个 SELECT 可以使用前导列为 source 的索引。


      跟进

      问:如果我使用的是 IN?比如 WHERE (id IN ('a','b') OR source IN ('a','b')) AND removed = 0

      A:我会使用相同的方法。

      SELECT u1.*
        FROM `uploads` u1
       WHERE u1.id     IN ('17D30437329A9B9 ', ... )
         AND u1.removed = 0
      
       UNION ALL
      
      SELECT u2.*
        FROM `uploads` u2
       WHERE u2.source IN ('17D30437329A9B9 ', ... )
         AND u2.removed = 0
         AND ( u2.id IS NULL OR u2.id NOT IN ('17D30437329A9B9 ', ... ) )
      

      第二个 SELECT 的最后一个条件是为了防止查询返回第一个 SELECT 已经返回的行。

      使用 NOT IN,只需确定列表中的 none 值为 NULL。 (如果 NOT IN 列表包含 NULL 值,则没有行将满足条件。)

      如果uploads 中的id 列保证为NOT NULL,则可以省去对NULL 的检查。 (我们没有看到任何表定义,所以我们无法判断 id 是否被定义为 NOT NULL,因此编写查询以在更一般的情况下工作,而不是基于可能的错误假设。)

      【讨论】:

      • 如果我使用的是IN?喜欢WHERE (id IN ('a','b') OR source IN ('a','b')) AND removed = 0
      • 我推荐同样的方法......一个 SELECT 执行“id IN ()”,另一个 SELECT 执行“source IN ()”。我们希望第一个能够使用前导列为id 的索引,第二个使用前导列为source 的索引。
      猜你喜欢
      • 2016-06-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多