【问题标题】:Improving SELECT performance in two unioned (UNION ALL) huge tables提高两个联合 (UNION ALL) 大表中的 SELECT 性能
【发布时间】:2017-05-15 15:14:07
【问题描述】:

我有两个巨大的表,需要执行以下查询。表authors_1(~20M 行)和authors_2(~120M 行)具有相同的结构。我已经完成了研究并提出了以下查询和表结构。查询仍然需要很长时间(通常在 10 到 20 秒之间)。

这是查询:

SELECT `fname`, `lname` 
FROM (
    SELECT `fname`, `lname` 
    FROM `authors_1` 
    WHERE 1 AND `lname` LIKE 'AR%' 
    UNION ALL 
    SELECT `fname`, `lname` 
    FROM `authors_2` 
    WHERE 1 AND `lname` LIKE 'AR%') `a` 
GROUP BY CONCAT(`fname`, `lname`) 
ORDER BY `lname` 
LIMIT 0, 999;

这是两个表的结构相似(FT 索引用于其他查询)。

CREATE TABLE `scipers_authors` (
 `a_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
 `linker` varchar(255) COLLATE utf8_persian_ci NOT NULL,
 `fname` tinytext COLLATE utf8_persian_ci NOT NULL, /*Should this be tinytext because of FT index or I can use VARCHAR(255) while having FT index?*/
 `lname` tinytext COLLATE utf8_persian_ci NOT NULL, /*Same for this one*/
 PRIMARY KEY (`a_id`),
 UNIQUE KEY `linker` (`linker`) USING BTREE,
 KEY `lname_4` (`lname`(4)) USING BTREE,
 KEY `name` (`lname`(128),`fname`(128)) USING BTREE,
 FULLTEXT KEY `fname` (`fname`),
 FULLTEXT KEY `lname` (`lname`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_persian_ci;

这是上面显示的示例查询的EXPLAIN 输出(我不知道如何使它漂亮like others,我正在使用PMA):

1   PRIMARY <derived2>  ALL                 476968  Using temporary; Using filesort 
2   DERIVED authors_1   range   lname_4,name,lname  name    386     184800  Using where 
3   UNION   authors_2   range   lname_4,name,lname  name    386     292168  Using where 
    UNION RESULT    <union2,3>  ALL                     Using temporary 

对改进这个查询/结构有什么建议吗?

【问题讨论】:

  • 我认为您不需要lname_4 索引,因为它是name 索引的前缀。请注意,改为使用 name 索引。有多少行满足LIKE 条件?
  • GROUP BY CONCAT(fname, lname) 改为 GROUP BY fname, lname 然后mysql可以使用索引(如果有的话)
  • +1 表示I've done my researches and came up.... 进行研究之前询问 SO 总是一个好兆头:-)。并且还显示EXPLAIN
  • @Barmar,对于给定的查询,它大约有 320K 行。我认为lname_4 可以提供帮助,因为它只索引了前 4 个字母。
  • B-tree 索引可用于优化索引的任何前缀。所以前缀的另一个索引是多余的。

标签: mysql union


【解决方案1】:

尝试将GROUP BY CONCAT(fname, lname) 更改为GROUP BY fname, lname。完成所有这些连接是不必要的额外工作。

它们之间唯一的语义区别是如果有两个人的名字和姓氏不同,但在连接时它们是相同的,例如弗雷达史密斯和弗雷德阿史密斯。这样的组合可能不太可能,而且我怀疑您是否真的希望将它们组合成一个结果。

如果您按您选择的同一组列进行分组,您可以简单地使用

SELECT DISTINCT fname, lname

您可以改用UNION DISTINCT 而不是UNION ALL,因为您希望删除重复项。那么你就不需要在外部查询中进行分组或DISTINCT

【讨论】:

  • +1 建议使用UNION DISTINCT。需要明确的是,如果你这样做了,那么GROUP BYSELECT DISTINCT是没有必要的。
  • 谢谢,我已经把它从“also”改为“instead”,并添加了更多解释。
  • @Barmar,我测试了你提出的解决方案。它确实改进了执行时间(从 53 秒到 25 秒 - 基于 PMA 显示的时间),但仍然需要很长时间。有没有改进的地方?
  • 其他搜索查询显示不同,有时执行时间更短。
  • 我从来没有真正理解使用TINYTEXT 的意义,因为你可以使用VARCHAR(255) 来做同样的事情,而且你不必解决文本类型的限制.
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-05
相关资源
最近更新 更多