【问题标题】:Why does this MySQL query slow down when more conditions are added?当添加更多条件时,为什么这个 MySQL 查询会变慢?
【发布时间】:2017-01-29 13:47:54
【问题描述】:

所以我有两张桌子:

1

CREATE TABLE `adstable` (
 `adid` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
 `adbudget` decimal(14,7) unsigned NOT NULL DEFAULT '0.0000000',
 `targetdesktop` tinyint(1) NOT NULL DEFAULT '1',
 `adactive` tinyint(1) NOT NULL,
 `user` bigint(20) unsigned NOT NULL,
 `approved` tinyint(1) NOT NULL,
 `imp_today` int(10) unsigned NOT NULL DEFAULT '0',
 `targetwindows` tinyint(1) NOT NULL,
 PRIMARY KEY (`adid`),
 KEY `user_index` (`user`),
 KEY `budget_index` (`adbudget`) USING BTREE,
 KEY `imp_today_index` (`imp_today`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=719102 DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT

2

CREATE TABLE `userstable` (
 `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
 `balance` decimal(14,7) unsigned NOT NULL DEFAULT '0.0000000',
 PRIMARY KEY (`id`),
 KEY `balance_index` (`balance`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT

我有两个 MySQL 查询:

1

SELECT adstable.adid FROM adstable INNER JOIN userstable ON (adstable.user = userstable.id  ) 
WHERE  
adstable.adactive=1 AND adstable.approved = 1 AND 
adstable.targetdesktop = 1 AND adstable.targetwindows = 1 and 
adstable.adbudget > 0.02 AND userstable.balance > 0.02
ORDER BY adstable.imp_today ASC limit 1

2

SELECT adstable.adid FROM adstable INNER JOIN userstable ON (adstable.user = userstable.id  ) 
WHERE  
adstable.adactive=1 AND adstable.approved = 1 AND 
adstable.adbudget > 0.02 AND userstable.balance > 0.02
ORDER BY adstable.imp_today ASC limit 1

第一个查询在 where 子句中有一个额外的条件:adstable.targetdesktop = 1 AND adstable.targetwindows = 1

但我无法理解的是为什么第一个查询需要 2-3 秒才能运行,而第二个查询需要 2-3 秒。

注意事项:

  • adstable 有大约 70 万行
  • 第二个和第一个查询都返回相同的 2 行(但额外的 第一个查询中的条件使它慢得多)
  • 我通过删除 adstable.adactive=1 AND adstable.approved 运行了查询 1 = 1 而不是 adstable.targetdesktop = 1 AND adstable.targetwindows = 1,查询运行时间为 0.001 秒。

有谁知道为什么第二个查询比第一个快得多,即使第二个返回的行数和第一个相同?

【问题讨论】:

  • 我的逻辑是:你使用的条件越多 -> 引擎需要做的比较越多 -> 需要更多的时间。确保主动和批准字段的字段类型是“合法的”(据我了解,它们应该是布尔值 - 例如不是 INT 或 VARCHAR)。
  • @OfirBaruch 我认为 tinyint(1) 和 boolean 是等价的?
  • 是的。您是否在 phpmyadmin 中测试查询?
  • @OfirBaruch 第一个查询与第二个查询相同,但有两个额外的比较。但是,如果没有这两个比较,第一个查询仍然会返回相同的行......所以我不明白开销(2 秒的差异)。是的 phpmyadmin
  • 即使返回相同的行 - 引擎仍然需要运行所有条件。很遗憾,我没有其他答案。

标签: php mysql indexing query-optimization


【解决方案1】:

您没有发布来自EXPLAIN 的输出,所以这是猜测。但似乎您的第一个查询的条件以某种方式迫使 MySQL 的查询计划器对您的adstable 进行全表扫描。这不是一张小桌子,所以扫描需要一段时间。

尝试在该表的(adactive, approved, adbudget, user) 上创建复合索引。这是您查询的覆盖索引。您应该阅读有关覆盖索引的内容。

 ALTER TABLE adactive 
   ADD INDEX act_appr_bud_user (adactive, approved, adbudget, user);

这应该允许查询规划器随机访问并部分扫描索引以满足您的查询。它可能会加快速度。

注意(截至 2017 年初)MySQL 的查询计划器通常只能为每个表使用一个索引来满足查询。因此,单个列上的大量索引不能像为特定查询选择的复合索引那样有帮助。

【讨论】:

    【解决方案2】:

    对于大表和LIMIT 1,诀窍是消耗整个WHERE,加上ORDER BY。这是不可能的,但这里的想法可能会有所帮助。

    WHERE  
    adstable.adactive=1 AND
    adstable.approved = 1 AND 
    adstable.targetdesktop = 1 AND
    adstable.targetwindows = 1 and 
    adstable.adbudget > 0.02 AND
    userstable.balance > 0.02
    ORDER BY adstable.imp_today ASC limit 1
    

    adstable需要

    INDEX(adactive, approved, targetdesktop, targetwindows, adbudget),
    INDEX(adactive, approved, targetdesktop, targetwindows, imp_today)
    

    前 4 列可以按任意顺序排列,但最后一列必须在最后。

    第一个索引完全处理WHEREadstable,但需要排序。第二个索引会做一些过滤,但要避免排序和短路LIMIT 1。优化器可能(也可能不会)选择两者中的更好者。

    您可以将它们中的每一个扩展为“覆盖”,但只能添加到列表的末尾。

    在这

    WHERE  
    adstable.adactive=1 AND
    adstable.approved = 1 AND 
    adstable.adbudget > 0.02 AND
    userstable.balance > 0.02
    ORDER BY adstable.imp_today ASC limit 1
    

    adstable需要

    INDEX(adactive, approved, adbudget),
    INDEX(adactive, approved, imp_today)
    

    前 2 列可以按任意顺序排列,但最后一列必须排在最后。同样,它们最终可以扩展,以使它们“覆盖”。

    如果您需要同时执行这两个查询,那么我建议您拥有所有 4 个索引;没有一个是最佳的。

    在这两种情况下,userstable 都需要INDEX(id, balance) 除非您已经拥有PRIMARY KEY(id)

    JOIN 可以替换为

    AND ( EXISTS * FROM userstable
             id = adstable.user AND balance > 0.02 )
    

    这可能(也可能不会)影响性能。

    My cookbook on indexing 中的更多内容。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-09-05
      • 2021-09-03
      • 2014-11-04
      • 1970-01-01
      • 1970-01-01
      • 2011-10-18
      • 2013-07-09
      • 2013-06-06
      相关资源
      最近更新 更多