为什么在添加更多条件时,这个MySQL查询会变慢?

时间:2017-01-29 13:47:54

标签: php mysql indexing query-optimization

所以我有两张桌子:

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有大约700k行
  • 第二个&第一个查询都返回相同的2行(但额外的 第一个查询中的条件使得它变得如此慢)
  • 我通过删除 adstable.adactive = 1 AND adstable.approved来运行查询1 = <1> adstable.targetdesktop = 1 AND adstable.targetwindows = 1 ,查询运行时间为0.001秒。

有没有人知道为什么第二个查询比第一个查询快得多,即使第二个查询返回的第一个#和第一个行的类型相同?

2 个答案:

答案 0 :(得分:0)

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

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

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

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

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

答案 1 :(得分:0)

对于大型表格和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列可以是任何顺序,但最后一列必须是最后一列。

第一个索引完全处理WHERE的{​​{1}},但需要排序。第二个索引将执行一些过滤,但避免排序和短路adstable。优化器可能(或可能不)选择两者中的较好者。

你可以将它们中的每一个扩展为&#34;覆盖&#34;,但只能添加到列表的末尾。

在此

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

前两列可以是任何顺序,但最后一列必须是最后一列。同样,他们最终可以扩展,使他们能够覆盖&#34;。

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

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

PRIMARY KEY(id)可以替换为

JOIN

这可能(或可能不会)影响表现。

更多My cookbook on indexing