dongwen1935
2017-01-29 13:47
浏览 182
已采纳

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

So I have two tables:

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

and I have two MySQL queries:

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

The 1st query has an extra condition in the where clause: 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.

Notes:

  • adstable has around 700k rows
  • the second & first query both return the same 2 rows (but the extra condition in the 1st query makes it so much slower)
  • 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.

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?

  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

2条回答 默认 最新

  • doujuan9698 2017-01-29 14:21
    已采纳

    You didn't post output from EXPLAIN so this is guesswork. 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. 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. 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. So lots of indexes on single columns can't help as much as compound indexes chosen for particular queries.

    已采纳该答案
    打赏 评论
  • douyao6842 2017-01-30 01:44

    For large tables and LIMIT 1, the trick is to get the entire WHERE consumed, plus, the 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

    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.

    The first index deals completely with the WHERE for adstable but will need to sort. The second index will do some of the filtering, but avoid the sort and short-circuit the 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

    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; no single one is optimal.

    In both cases, userstable needs INDEX(id, balance) unless you already have PRIMARY KEY(id).

    The JOIN could be replaced by

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

    This may (or may not) affect performance.

    More in My cookbook on indexing .

    打赏 评论

相关推荐 更多相似问题