了解为啥有大量文本列时按查询分组会变慢

Posted

技术标签:

【中文标题】了解为啥有大量文本列时按查询分组会变慢【英文标题】:Understanding why group by query slows down when there are lots of text columns了解为什么有大量文本列时按查询分组会变慢 【发布时间】:2019-04-07 15:34:47 【问题描述】:

我有一个运行缓慢的查询,我想出了一个更快的替代方案,但我希望得到一些帮助,以了解为什么原始查询如此缓慢。

我的问题的简化版本使用两个表。第一个表的简化版本,称为配置文件,是

`profiles` (
 `id` int(11),
 `title` char(255),
 `body` text,
 `pin` int(11),
  PRIMARY KEY (`id`),
  UNIQUE KEY `pin` (`pin`)
 )

我的第二个表调用的简化版本是

`calls` (
 `id` int(11),
 `pin` int(11),
 `duration` int(11),
 PRIMARY KEY (`id`),
 KEY `ivr_id` (`pin`)
)

我的查询应该获得完整的个人资料,以及个人资料收到的电话数量。我使用的查询是

SELECT profiles.*, COUNT(*) AS num_calls 
FROM profiles 
LEFT JOIN calls 
ON profiles.pin = calls.pin
GROUP BY profiles.pin

对于大约 100 个配置文件和大约 250,000 个调用,此查询大约需要 10 秒,这很慢。

如果我将查询修改为仅从配置文件中选择标题,而不是所有列,则查询速度会快得多。如果我修改查询以删除分组依据,它也会快得多。如果我只是从配置文件表中选择所有内容,那么它也是一个快速查询。

我的实际个人资料表有更多的文本和字符字段。选择的文本字段越多,查询速度越差。为什么文本字段不参与 JOIN 或 GROUP 时导致查询如此缓慢?

我想出了一个稍微不同的查询,它要快得多,不到半秒。这个查询是:

SELECT profiles.*, temp.readings 
FROM profiles 
LEFT JOIN (
    SELECT pin ,COUNT(*) AS readings 
    FROM calls 
    GROUP BY pin
) AS temp 
ON temp.pin=profiles.pin

虽然我认为我已经解决了速度问题,但我想了解在第一个查询中导致问题的原因。

======== 更新========

我刚刚分析了这两个查询,整个速度差异都在“发送数据”部分。慢查询10秒左右,快查询0.1秒左右

======== 更新 2 ========

在与@scaisEdge 讨论后,我想我可以重新表述我的问题。考虑一个有约 40 列的表 T1,其中 8 列是 TEXT 类型和约 100 行,表 T2 有 5 列 INT 和 VARCHAR 类型,约 250,000 行。为什么会这样:

SELECT T1.* FROM T1 is fast

SELECT T1.* FROM T1 JOIN T2 GROUP BY T1.joinfield is slow

如果选择字段是 INT 或 VARCHAR,SELECT T1.selectfield FROM T1 JOIN T2 GROUP BY T1.joinfield 会很快

【问题讨论】:

【参考方案1】:

这应该会发生,因为

第一个查询加入 100 个配置文件和 250,000 个调用,然后根据结果减少返回的行分组。并且 select profile.* 意味着每个匹配行对配置文件表数据的完全访问

然后第二个查询将 100 个配置文件与 TEMP 的子查询返回的行数(可能远小于 250,000)相结合,减少了对表配置文件数据的访问次数

而不是个人资料。*尝试仅访问固定列

SELECT profiles.pin, COUNT(*) AS num_calls 
FROM profiles 
LEFT JOIN calls ON profiles.pin = calls.pin
GROUP BY profiles.pin

作为一个建议,您应该注意,对于第一个查询使用 group by 只允许 mysql 版本早于版本 5.7 .. 因为在 select 子句中使用 group by 列而不提及列并且不受聚合函数的影响并且默认不允许在 GROUP BY 中提及并产生错误..

【讨论】:

我不明白的部分答案是“完全访问”。为什么我访问的配置文件表中有多少列很重要?当 MySQL 进行连接时,它是否会创建一个临时表,其中包含配置文件中所有数据的完整副本? 1) 当您使用选择表时。*必须检索列的所有值以填充选择结果..因此对于存储在特定查询中使用的索引中的列值,值是直接检索 .. 对于其他值,这些值在表数据中被 fecthed .. 你选择所有列的事实也是如此 .. 然后所有这些值必须在每次访问表数据时检索 .. 2) 不完全是。当 mysql 执行连接(不调用 sibquery)时,db 引擎创建内部引用以检索所需的信息。当引擎创建了必要的引用时,它执行从索引或表或两者中获取数据..希望这很清楚.. 对不起,我还是不明白。如果内部连接只是使用引用,那么为什么 table.* 查询需要更长的时间,因为数据没有被多次复制?我会用更好的解释来更新我的问题。 因为必须从表中检索每列的值.. 更多列更多行你有更多时间消耗.. 如果索引中有这些值,则不会从表中检索这些值。 .

以上是关于了解为啥有大量文本列时按查询分组会变慢的主要内容,如果未能解决你的问题,请参考以下文章

为啥我们在 SQL Server 中透视文本列时使用 Max 函数?

为啥在视图中使用此查询会变慢?

为啥 MySQL 查询在使用 LIMIT 和 Order BY 时会变慢?

为什么在循环访问DataTable列时第二种方法会变慢

了解 Oracle 别名 - 为啥在查询中不识别别名,除非包装在第二个查询中?

文本内容查询