mysql查询优化:选择带计数的子查询极慢

时间:2017-09-21 23:50:13

标签: mysql sql query-optimization

我有以下表格:

mysql> show create table rsspodcastitems \G
*************************** 1. row ***************************
       Table: rsspodcastitems
Create Table: CREATE TABLE `rsspodcastitems` (
  `id` char(20) NOT NULL,
  `description` mediumtext,
  `duration` int(11) default NULL,
  `enclosure` mediumtext NOT NULL,
  `guid` varchar(300) NOT NULL,
  `indexed` datetime NOT NULL,
  `published` datetime default NULL,
  `subtitle` varchar(255) default NULL,
  `summary` mediumtext,
  `title` varchar(255) NOT NULL,
  `podcast_id` char(20) NOT NULL,
  PRIMARY KEY  (`id`),
  UNIQUE KEY `podcast_id` (`podcast_id`,`guid`),
  UNIQUE KEY `UKfb6nlyxvxf3i2ibwd8jx6k025` (`podcast_id`,`guid`),
  KEY `IDXkcqf7wi47t3epqxlh34538k7c` (`indexed`),
  KEY `IDXt2ofice5w51uun6w80g8ou7hc` (`podcast_id`,`published`),
  KEY `IDXfb6nlyxvxf3i2ibwd8jx6k025` (`podcast_id`,`guid`),
  KEY `published` (`published`),
  FULLTEXT KEY `title` (`title`),
  FULLTEXT KEY `summary` (`summary`),
  FULLTEXT KEY `subtitle` (`subtitle`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

mysql> show create table station_cache \G
*************************** 1. row ***************************
       Table: station_cache
Create Table: CREATE TABLE `station_cache` (
  `Station_id` char(36) NOT NULL,
  `item_id` char(20) NOT NULL,
  `item_type` int(11) NOT NULL,
  `podcast_id` char(20) NOT NULL,
  `published` datetime NOT NULL,
  KEY `Station_id` (`Station_id`,`published`),
  KEY `IDX12n81jv8irarbtp8h2hl6k4q3` (`Station_id`,`published`),
  KEY `item_id` (`item_id`,`item_type`),
  KEY `IDXqw9yqpavo9fcduereqqij4c80` (`item_id`,`item_type`),
  KEY `podcast_id` (`podcast_id`,`published`),
  KEY `IDXkp2ehbpmu41u1vhwt7qdl2fuf` (`podcast_id`,`published`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)

" item_id"第二列是指" id"前者的列(两者之间没有外键,因为这种关系是多态的,即第二个表可能引用了第一个但不在其他相似但不同的表中的实体)

我试图获取一个查询,列出第一个表中第二个中没有任何相应项的最新项。我到目前为止发现的最高性能查询是:

select i.*, 
       (select count(station_id) 
         from station_cache 
          where item_id =  i.id) as stations 
 from rsspodcastitems i 
  having stations = 0 
   order by published desc

我还考虑使用where not exists (...)子查询来执行限制,但这实际上比我上面的更慢。但这仍需要相当长的时间才能完成。 MySQL的查询计划似乎没有使用可用的索引:

+----+--------------------+---------------+------+---------------+------+---------+------+--------+----------------+
| id | select_type        | table         | type | possible_keys | key  | key_len | ref  | rows   | Extra          |
+----+--------------------+---------------+------+---------------+------+---------+------+--------+----------------+
|  1 | PRIMARY            | i             | ALL  | NULL          | NULL | NULL    | NULL | 106978 | Using filesort |
|  2 | DEPENDENT SUBQUERY | station_cache | ALL  | NULL          | NULL | NULL    | NULL |  44227 | Using where    |
+----+--------------------+---------------+------+---------------+------+---------+------+--------+----------------+

请注意,查询的任何部分都不使用key,而应该能够使用主表中的KEY published (published)和子查询中的KEY item_id (item_id,item_type)

有关如何在不等待几分钟的情况下获得适当结果的任何建议吗?

3 个答案:

答案 0 :(得分:1)

我希望最快的查询是:

select i.*
from rsspodcastitems i 
where not exists (select 1
                  from station_cache sc
                  where sc.item_id = i.id
                 )
order by published desc;

这将利用station_cache(item_id)rsspodcastitems(published, id)上的索引。

如果查询返回大量行,您的查询可能会更快。您对查询的措辞允许rsspodcastitems(published)上的索引避免文件排序。如果您删除group by,则exists版本应该更快。

我应该注意到我喜欢你使用having子句。在过去遇到这个问题时,我使用了一个子查询:

select i.*, 
       (select count(station_id) 
         from station_cache 
          where item_id =  i.id) as stations 
from (select i.*
      from rsspodcastitems i 
      order by published desc
     ) i
where not exists (select 1
                  from station_cache sc
                  where sc.item_id = i.id
                 );

这允许一个索引进行排序。

我更喜欢你的方法略有不同:

select i.*, 
       (exists (select 1 
                from station_cache sc
                where sc.item_id = i.id
               )
       ) as has_station 
from rsspodcastitems i 
having has_station = 0 
order by published desc;

这应该比使用count()的版本稍快一些。

答案 1 :(得分:0)

您可能希望从表中检测并删除冗余索引。查看两个表的CREATE TABLE信息,并帮助您发现多个表,包括podcast_idguidStation_idpublisheditem_iditem_typepodcast_idpublished可能会有更多。

答案 2 :(得分:0)

我最终的解决方案是删除全文索引并使用外部生成的索引表(通过迭代文本中的单词,过滤停用词和应用词干算法)来允许搜索。我不知道为什么全文索引会导致性能问题,但它们似乎会减慢每个触及表格的查询,即使它们没有被使用过。