选择VARCHAR时MySQL查询速度很慢

时间:2010-12-05 00:34:32

标签: sql mysql

我有这张桌子:

CREATE TABLE `search_engine_rankings` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `keyword_id` int(11) DEFAULT NULL,
  `search_engine_id` int(11) DEFAULT NULL,
  `total_results` int(11) DEFAULT NULL,
  `rank` int(11) DEFAULT NULL,
  `url` varchar(255) DEFAULT NULL,
  `created_at` datetime DEFAULT NULL,
  `updated_at` datetime DEFAULT NULL,
  `indexed_at` date DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `unique_ranking` (`keyword_id`,`search_engine_id`,`rank`,`indexed_at`),
  KEY `search_engine_rankings_search_engine_id_fk` (`search_engine_id`),
  CONSTRAINT `search_engine_rankings_keyword_id_fk` FOREIGN KEY (`keyword_id`) REFERENCES `keywords` (`id`) ON DELETE CASCADE,
  CONSTRAINT `search_engine_rankings_search_engine_id_fk` FOREIGN KEY (`search_engine_id`) REFERENCES `search_engines` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=244454637 DEFAULT CHARSET=utf8 

它在生产中有大约250M行。

当我这样做时:

select id,
       rank 
  from search_engine_rankings 
 where keyword_id = 19000 
   and search_engine_id = 11 
   and indexed_at = "2010-12-03";

......它运行得非常快。

当我添加url列(VARCHAR)时:

select id,
       rank,
       url 
  from search_engine_rankings 
 where keyword_id = 19 
   and search_engine_id = 11 
   and indexed_at = "2010-12-03";

......它运行得很慢。

有什么想法吗?

4 个答案:

答案 0 :(得分:1)

单独的索引可以满足第一个查询 - 无需读取基表来获取Select子句中的值。第二个语句需要读取基表,因为URL列不是索引的一部分。

 UNIQUE KEY `unique_ranking` (`keyword_id`,`search_engine_id`,`rank`,`indexed_at`),

基本表中的行与索引中的行的物理顺序不同,因此基表的读取可能涉及相当大的磁盘抖动。

您可以将其视为优化的一种证明 - 在第一个查询中,磁盘抖动是避免,因为引擎足够聪明,可以查询索引中所请求的值选择条款;它已经将该索引读入RAM中的where子句,因此它利用了这一事实。

答案 1 :(得分:1)

除了蒂姆的回答。 Mysql中的索引只能从左到右使用。这意味着它可以在WHERE子句中使用索引的列,直到您使用它们为止。

目前,您的UNIQUE索引为keyword_idsearch_engine_idrankindexed_at。这将能够过滤keyword_idsearch_engine_id列,仍然需要扫描其余行以过滤indexed_at

但如果您将其更改为:keyword_idsearch_engine_idindexed_atrank(只是订单)。这样就可以过滤列keyword_idsearch_engine_idindexed_at

我相信它能够完全使用该索引来读取表格的相应部分。

答案 2 :(得分:0)

我知道这是一个很老的帖子,但我遇到了同样的情况,我没有找到答案。 这确实发生在MySQL中,当你有varchar列时,它需要花费大量的时间处理。我的查询花了大约20秒处理1.7M行,现在大约是1.9秒。

好的,首先,从这个查询中创建一个视图:

CREATE VIEW view_one AS 
  select id,rank 
  from search_engine_rankings 
  where keyword_id = 19000 
  and search_engine_id = 11 
  and indexed_at = "2010-12-03";

其次,相同的查询,但内部联接:

select v.*, s.url 
from view_one AS v 
inner join search_engine_rankings s ON s.id=v.id;

答案 3 :(得分:0)

TLDR:我通过在表上运行optimize解决了这个问题。


我刚才也经历过。即使在主键上查找并仅选择一些行也很慢。经过测试,我发现它不仅限于varchar列,选择一个int也花费了大量时间。

大致像这样的查询大约需要3秒钟:

select someint from mytable where id in (1234, 12345, 123456)

虽然查询大致像这样花费了<10毫秒:

select count(*) from mytable where id in (1234, 12345, 123456)

这里批准的答案是也要建立一个跨越someint的索引,这样做会很快,因为mysql可以从索引中获取它需要的所有信息,而不必触摸表。这可能在某些设置中有效,但是我认为这是一个愚蠢的解决方法-显然是错误的,从表中获取三行不需要三秒钟!此外,大多数应用程序只是执行“从mytable中选择*”,并且在应用程序端进行更改并不总是那么简单。

optimize table之后,两个查询都需要10ms。