MySQL 中的 UNION ALL 性能不佳

Posted

技术标签:

【中文标题】MySQL 中的 UNION ALL 性能不佳【英文标题】:Poor UNION ALL performance in MySQL 【发布时间】:2016-08-06 13:59:26 【问题描述】:

我有一个包含如下行的数据库:

+------------+---------+------------+-------+
| continent  | country | city       | value |
+------------+---------+------------+-------+
| Asia       | China   | Beijing    | 3     |
| ...        | ...     | ...        | ...   |
| N. America | USA     | D.C        | 7     |
| ....       | ....    | ....       | ....  |

为了生成树状图可视化,我需要将其处理成具有以下形状的表格:

+-----+------------+-------+
| uid | parent-uid | value |
+-----+------------+-------+

在这种情况下,AsiaChina 的“父级”,它是 Beijing 的“父级”。所以对于这三个你会有 something 比如:

+---------+--------+-----+
| Beijing | China  | 3   |
| China   | Asia   | ... |
| Asia    | global | ... |
+---------+--------+-----+

China 的“值”需要是所有子值的聚合。同样,Asia 的值需要是所有子值的聚合。

为了纯粹在 SQL 中完成此任务,我创建了以下三个查询并将它们与 UNION ALL 结合起来:

# City-level:
SELECT
     CONCAT(continent, "-", country, "-", city) as uid,
     CONCAT(continent, "-", country) as parentuid,
     value
FROM
     table

UNION ALL

# Country-level
SELECT
     CONCAT(continent, "-", country) as uid,
     continent as parentuid,
     SUM(value) as value
FROM
     table
GROUP BY
     country

UNION ALL

# Continent-level
SELECT
    continent as uid,
    "global" as parentuid,
    SUM(value) as value
FROM
    table
GROUP BY
    continent

每个单独的查询都在毫秒内完成。市级、***、大洲级全部返回结果

当我将它们结合在一起时,突然需要 8 秒才能得到结果!

我试过用谷歌搜索这些问题,但一切都只是说“使用UNION ALL 而不是UNION”(我已经是了)

我认为它可能没有足够的 RAM 来构建临时结果表,所以它是磁盘垃圾,但我不知道如何增加内存限制。我尝试将 innodb_buffer_pool_size 提高到 1GB (1073741824) 但没有帮助

【问题讨论】:

你确定是查询让事情变慢了,而不是结果集的传递或呈现? 表格有多少行?输出有多少行? 你有多少内存?你用的是哪个版本的mysql? (此处涉及的隐式临时表可能存在问题。) 在需要 8 秒时,您能判断您是否受 I/O 限制吗? "SELECT SQL_NO_CACHE ...重新计时。 【参考方案1】:

第一个select,选择表中的所有行然后获取第一行非常快,但获取所有行将花费很多时间(mysql工作台默认将limit 1000附加到查询末尾)。

要测试获取所有行需要更多时间,请尝试以下查询并告诉我们它消耗的时间:

select * from (
SELECT
     CONCAT(continent, "-", country, "-", city) as uid,
     CONCAT(continent, "-", country) as parentuid,
     value
FROM
     table
) t1;

如果它需要将近 8 秒,那么你的工会没有问题。为了提高性能,您必须使用 where 子句来限制行数。

希望对你有帮助。

【讨论】:

果然,这解释了性能。我使用了您推荐的 select * from (...) 查询,它从 0.0027 秒变为 9.3 秒。猜猜 MySQL 工作台让我很困惑。 @stevendesu 很高兴,我可以帮忙【参考方案2】:

我想我的问题是:WITH ROLLUP 有什么问题?

SELECT
    CONCAT_WS('-',continent,country,city) as uid,
    CONCAT_WS('-',continent,COALESCE(country,'global')) as parentuid,
    value
FROM (
    SELECT continent, country, city, SUM(value) as value
    FROM table
    GROUP BY continent, country, city WITH ROLLUP
) t1
WHERE t1.continent IS NOT NULL;

我的CONCAT_WS() 调用可能不正确,特别是如果您有名为'' 的城市或国家/地区,但我不得不认为这会更快。 WHERE 子句只是用来删除整体摘要。

这是 MySQL 文档中WITH ROLLUP 的示例,以帮助解释它的作用:

mysql> SELECT year, country, product, SUM(profit)
    -> FROM sales
    -> GROUP BY year, country, product WITH ROLLUP;
+------+---------+------------+-------------+
| year | country | product    | SUM(profit) |
+------+---------+------------+-------------+
| 2000 | Finland | Computer   |        1500 |
| 2000 | Finland | Phone      |         100 |
| 2000 | Finland | NULL       |        1600 |
| 2000 | India   | Calculator |         150 |
| 2000 | India   | Computer   |        1200 |
| 2000 | India   | NULL       |        1350 |
| 2000 | USA     | Calculator |          75 |
| 2000 | USA     | Computer   |        1500 |
| 2000 | USA     | NULL       |        1575 |
| 2000 | NULL    | NULL       |        4525 |
| 2001 | Finland | Phone      |          10 |
| 2001 | Finland | NULL       |          10 |
| 2001 | USA     | Calculator |          50 |
| 2001 | USA     | Computer   |        2700 |
| 2001 | USA     | TV         |         250 |
| 2001 | USA     | NULL       |        3000 |
| 2001 | NULL    | NULL       |        3010 |
| NULL | NULL    | NULL       |        7535 |
+------+---------+------------+-------------+

【讨论】:

WITH ROLLUP 可能完全符合我的需要(我什至不知道这是一件事)。我会在星期一试一试,看看会发生什么 刚刚对此进行了测试,实际上使用WITH ROLLUP 比我的UNION ALL 解决方案花费的时间略更长(~11 秒而不是~8 秒)。我怀疑这是因为在最低级别(当不使用分组依据时)我直接选择值而不是选择聚合。奇怪的是,WITH ROLLUP 返回了 275000 行,而我的 UNION ALL 解决方案返回了 250000 行。我不知道新行的全部内容。也许与澳大利亚有些怪癖(大陆/国家相同)?

以上是关于MySQL 中的 UNION ALL 性能不佳的主要内容,如果未能解决你的问题,请参考以下文章

带你搞懂mysql中的union(all)limitexists关键字

MySQL 中的 UNION ALL 和 LIMIT

如何简化mysql中的多个UNION ALL?

结合 UNION ALL 的表的 VIEW 的 MySQL 性能

mysql union all和union的区别

MySQL UNION ALL 性能调优