简体   繁体   English

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

[英]Why does this MySQL query slow down when more conditions are added?

So I have two tables: 所以我有两张桌子:

1 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 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

and I have two MySQL queries: 我有两个MySQL查询:

1 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 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

The 1st query has an extra condition in the where clause: adstable.targetdesktop = 1 AND adstable.targetwindows = 1 第一个查询在where子句中有一个额外条件: adstable.targetdesktop = 1 AND adstable.targetwindows = 1

However the thing that I can't understand is why the first query takes 2-3 seconds to run, while the second query takes 2-3 seconds. 然而,我无法理解的是为什么第一个查询需要2-3秒才能运行,而第二个查询需要2-3秒。

Notes: 笔记:

  • adstable has around 700k rows adstable有大约700k行
  • the second & first query both return the same 2 rows (but the extra condition in the 1st query makes it so much slower) 第二个和第一个查询都返回相同的2行(但第一个查询中的额外条件使它变得如此慢)
  • I ran query 1 by removing adstable.adactive=1 AND adstable.approved = 1 instead of adstable.targetdesktop = 1 AND adstable.targetwindows = 1 , and the query ran in 0.001 seconds. 我通过删除adstable.adactive = 1 AND adstable.approved = 1而不是 adstable.targetdesktop = 1 AND adstable.targetwindows = 1来运行查询1 ,并且查询在0.001秒内运行。

Does anyone know why the second query is so much faster than the first, even though the second returns the same # and type of rows the 1st does? 有谁知道为什么第二个查询比第一个查询快得多,即使第二个查询返回第1个#和第1行的类型?

You didn't post output from EXPLAIN so this is guesswork. 你没有从EXPLAIN发布输出,所以这是猜测。 But it seems possible the conditions of your first query are somehow forcing MySQL's query planner to do a full table scan of your adstable . 但是,您的第一个查询的条件似乎可能以某种方式迫使MySQL的查询规划器对您的adstable进行全表扫描。 It's not a small table, so the scan will take a while. 它不是一张小桌子,所以扫描需要一段时间。

Try creating a compound index on (adactive, approved, adbudget, user) on that table. 尝试在该表上创建复合索引(adactive, approved, adbudget, user) That's a covering index for your query. 这是您的查询的覆盖索引。 You should read about covering indexes. 你应该读一下覆盖索引。

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

That should allow the query planner to random-access and the partially scan an index to satisfy your query. 这应该允许查询计划程序随机访问并部分扫描索引以满足您的查询。 It will likely speed things up. 它可能会加快速度。

Notice (as of early 2017) MySQL's query planner ordinarily can use only one index per table to satisfy a query. 注意(截至2017年初)MySQL的查询规划器通常每个表只能使用一个索引来满足查询。 So lots of indexes on single columns can't help as much as compound indexes chosen for particular queries. 因此,单列上的大量索引无法帮助为特定查询选择的复合索引。

For large tables and LIMIT 1 , the trick is to get the entire WHERE consumed, plus, the ORDER BY . 对于大型表和LIMIT 1 ,诀窍是获取整个WHERE消耗,以及ORDER BY That is not possible, but the ideas here may help. 这是不可能的,但这里的想法可能会有所帮助。

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 needs adstable需求

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

The first 4 columns can be in any order, but the last needs to be last. 前4列可以是任何顺序,但最后一列必须是最后一列。

The first index deals completely with the WHERE for adstable but will need to sort. 第一个索引完全处理adstableWHERE ,但需要排序。 The second index will do some of the filtering, but avoid the sort and short-circuit the LIMIT 1 . 第二个索引将执行一些过滤,但避免LIMIT 1的排序和短路。 The Optimizer may (or may not) pick the better of the two. 优化器可能(或可能不)选择两者中的较好者。

You could extend each of them to be "covering", but only by adding onto the end of the list. 您可以将它们中的每一个扩展为“覆盖”,但只能添加到列表的末尾。

In this 在这

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 needs adstable需求

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

The first 2 columns can be in any order, but the last needs to be last. 前两列可以是任何顺序,但最后一列必须是最后一列。 Again, they could be extended, on the end, to make them "covering". 同样,它们最终可以延伸,使它们“覆盖”。

If you need to do both queries, then I recommend having all 4 indexes; 如果你需要进行两个查询,那么我建议使用所有4个索引; no single one is optimal. 没有一个是最佳的。

In both cases, userstable needs INDEX(id, balance) unless you already have PRIMARY KEY(id) . 在这两种情况下, 除非您已经拥有PRIMARY KEY(id) INDEX(id, balance) 否则 userstable需要INDEX(id, balance) PRIMARY KEY(id)

The JOIN could be replaced by JOIN可以替换为

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

This may (or may not) affect performance. 这可能(或可能不会)影响性能。

More in My cookbook on indexing . 更多关于索引的我的食谱

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM