简单选择组使用filesort&暂时的,不是索引

时间:2017-02-18 12:49:39

标签: mysql sql

所以我梳理了网络,似乎无法找到答案。我有一个具有以下结构的表

Table structure for table `search_tags`
--

CREATE TABLE IF NOT EXISTS `search_tags` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `LOOK_UP_TO_CAT_ID` int(11) NOT NULL,
  `SEARCH_TAG` text COLLATE utf8_unicode_520_ci NOT NULL,
  `DATE` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `SOURCE` varchar(225) COLLATE utf8_unicode_520_ci NOT NULL,
  `SOURCE_ID` int(11) NOT NULL,
  `WEIGHT` int(11) NOT NULL DEFAULT '1000',
  PRIMARY KEY (`ID`),
  KEY `LOOK_UP_TO_CAT_ID` (`LOOK_UP_TO_CAT_ID`),
  KEY `WEIGHT` (`WEIGHT`),
  FULLTEXT KEY `SEARCH_TAG` (`SEARCH_TAG`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_520_ci AUTO_INCREMENT=1 ;

该表位于800000多行并且正在增长。

当我在LOOK_UP_TO_CAT_ID上使用组运行查询时,查询运行需要1-2秒。我需要通过连接到其他表来运行此基础的多个版本,但这似乎是瓶颈所在,因为添加连接到这不会减慢它

    SELECT LOOK_UP_TO_CAT_ID, WEIGHT
FROM  `search_tags` 
WHERE  `SEARCH_TAG` LIKE  '%metallica%'
GROUP BY  `LOOK_UP_TO_CAT_ID`

删除GROUP BY会将查询时间降低到0.1,这似乎更容易接受,但后来我重复了。

在分组中使用说明表明它创建了一个临时表而不是使用索引

+----+-------------+-------------+------+-------------------+------+---------+------+--------+----------------------------------------------+--+
| id | select_type |    table    | type |   possible_keys   | key  | key_len | ref  |  rows  |                    Extra                     |  |
+----+-------------+-------------+------+-------------------+------+---------+------+--------+----------------------------------------------+--+
|  1 | SIMPLE      | search_tags | ALL  | LOOK_UP_TO_CAT_ID | NULL | NULL    | NULL | 825087 | Using where; Using temporary; Using filesort |  |
+----+-------------+-------------+------+-------------------+------+---------+------+--------+----------------------------------------------+--+

所以我不确定mysql是否在这里做正确的事情,但至少对我来说,不使用索引似乎是错误的。加快查询速度的最佳原因是什么?

编辑:

以下是我的数据示例:

+----+-------------------+----------------------------------+------------+---------------+-----------+--------+
| ID | LOOK_UP_TO_CAT_ID |            SEARCH_TAG            |    DATE    |    SOURCE     | SOURCE_ID | WEIGHT |
+----+-------------------+----------------------------------+------------+---------------+-----------+--------+
|  1 |               521 | METALLICA                        | 2017-02-18 | artist        |        15 |      1 |
|  2 |               521 | METALLICA - NOTHING ELSE MATTERS | 2017-02-18 | tracklisting  |        22 |      2 |
|  3 |               522 | METALLICA                        | 2017-02-18 | artist        |        15 |      1 |
|  4 |               522 | METALLICA - ST. Anger            | 2017-02-18 | product_title |       522 |      2 |
+----+-------------------+----------------------------------+------------+---------------+-----------+--------+

期望的结果

+-------------------+--------+
| LOOK_UP_TO_CAT_ID | WEIGHT |
+-------------------+--------+
|               521 |      1 |
|               522 |      1 |
+-------------------+--------+

2 个答案:

答案 0 :(得分:0)

在某种程度上,你的问题没有意义。您有一个全文索引,但正在使用LIKE进行表扫描。您需要使用MATCH()来使用全文索引。

我真正想到的是返回的数据量很大。在没有order bygroup by的情况下执行查询时,会在生成结果时返回结果。您会看到结果,因为早期扫描的某些行符合您的条件。

group by / order by需要阅读所有结果。

您可以通过count(*)代替select

来检查这一点
SELECT COUNT(*)
FROM `search_tags` 
WHERE `SEARCH_TAG` LIKE '%metallica%';

我怀疑这可能需要更长时间。

您可以使用相关子查询来消除重复消除的性能损失:

SELECT st.LOOK_UP_TO_CAT_ID, st.WEIGHT
FROM `search_tags` st
WHERE `SEARCH_TAG` LIKE  '%metallica%' AND
      st.id = (SELECT MIN(st2.id) FROM search_tags st2 WHERE st2.LOOK_UP_TO_CAT_ID = st.LOOK_UP_TO_CAT_ID);

这特别需要search_tags(LOOK_UP_TO_CAT_ID, ID)的索引来提高性能。

但是,您可能希望使用MATCH()来利用全文索引。

答案 1 :(得分:0)

为您提供一些建议。

    在这个悲惨的世界中,
  1. SEARCH_TAG LIKE '%metallica%'永远不会使用索引。模式haystack like '%needle'(前导%)要求MySQL检查列中的每个值以进行匹配。 haystack LIKE 'needle%'(尾随%)没有此问题。

  2. 您的SEARCH_TAG列上有a FULLTEXT index,因此请使用它! WHERE MATCH('metallica') AGAINST SEARCH_TAG是您需要的WHERE子句的形式。

  3. 您的表上有很多单列索引。这些通常对于提高查询速度没有帮助,除非它们恰好与您尝试做的完全匹配。您最好使用专为您正在运行的查询设计的compound covering indexes

  4. 您问题中的示例查询是

       SELECT LOOK_UP_TO_CAT_ID, WEIGHT
        FROM  search_tags 
       WHERE  SEARCH_TAG LIKE  '%metallica%'
    GROUP BY  LOOK_UP_TO_CAT_ID
    

    如果你把它改成这样,它会使SQL更有意义并且运行得更快。

       SELECT  LOOK_UP_TO_CAT_ID, MAX(WEIGHT)
         FROM  search_tags 
        WHERE  SEARCH_TAG LIKE  'metallica%'
     GROUP BY  LOOK_UP_TO_CAT_ID
    

    (注意我摆脱了领先的%。)

    如果在(SEARCH_TAG, LOOK_UP_CAT_ID, WEIGHT)上添加覆盖索引的化合物,此查询将变得非常快。可以从索引中满足整个查询。 MySQL随机访问索引以查找您的SEARCH_TAG,然后执行loose index scan以获取您请求的结果。

    (旁白:当您在filesortEXPLAIN查询的GROUP BY输出中看到ORDER BY时,请不要担心。它的一部分MySQL满足查询的方式。filesort中的文件并不一定意味着硬盘上的文件速度很慢。)