【问题标题】:Weird behavior of SELECT DISTINCT statement with RDS带有 RDS 的 SELECT DISTINCT 语句的奇怪行为
【发布时间】:2021-05-16 16:07:40
【问题描述】:

我创建了一个 2TB 的 MySQL RDS,并在其中填充了 2 个总共 1.5TB 的表:

+----------+---------------------------+------------+
| Database | Table                     | Size in MB |
+----------+---------------------------+------------+
| stam_db  | owl                       | 1182043.00 |
| stam_db  | owl_owners                |  393695.00 |

实例设置为 db.m6g.2xlarge 大小和 6000 预置 IOPS。

我运行此查询以返回前 10 行(它们都是不同的,没有重复的行):

SELECT  DISTINCT *
FROM    owl
ORDER BY
        name
LIMIT 10;

令我惊讶的是,这个查询已经运行了 2 个小时... 更令人惊讶的是,AWS 的“可用存储空间”指标开始以 2.2GB/分钟的速度下降:

由于某种原因,Write IOPS 突然上升到每秒 600-700:

读取 IOPS 甚至更高,达到每秒 1850 次:

这使总 IOPS 达到 2400-2500 左右:

CPU 利用率保持在低个位数:

我有几个问题:

  1. 为什么 SELECT DISTINCT 语句会导致对数据库进行如此大量的写入?
  2. 为什么 SELECT DISTINCT 会尝试读取整个数据库,而不仅仅是前 10 行?
  3. 为什么 RDS 不使用 6000 分配的 IOPS?总 IOPS 仅为分配量的 40% 左右。

答案如下:

Q2) 我想我在https://www.percona.com/blog/2019/07/17/mysql-disk-space-exhaustion-for-implicit-temporary-tables/ 找到了解释-" 大部分时间需要排序阶段的查询需要依赖临时表。例如,当您使用 GROUP BY、ORDER BY 或 DISTINCT 时。这样的查询分两个阶段执行:第一个是收集数据并将它们放入临时表中,第二个是对临时表执行排序。”因此,即使是带有 ORDER BY 的常规 SELECT 也需要重新读取整个表

Q1) 大量写入是为查询创建的临时表造成的,可以达到原表的100%。

Q3) 看起来 MySQL 代码创建临时表的效率不足以利用整个 6000 IOPS

【问题讨论】:

  • LIMIT 10 只返回 10 行作为结果,但在返回前 10 行之前对所有行进行 seacrh
  • 您的意思是 SELECT DISTINCT 在返回请求的行数之前读取整个表吗?常规的 SELECT 也有这种行为吗?
  • 是的...这是常规行为 .. 有 distinct 子句或没有 distinct 子句 .. distinct 子句只是避免 resutl 中的重复行
  • 哇...有没有办法在重新读取整个表格的情况下获得 10 行?
  • 我想我在percona.com/blog/2019/07/17/…找到了解释-" 大部分时间需要排序阶段的查询需要依赖临时表。例如,当你使用GROUP BY时,ORDER BY或 DISTINCT。这样的查询分两个阶段执行:第一个是收集数据并将它们放入临时表中,第二个是对临时表执行排序。因此,即使是带有 ORDER BY 的常规 SELECT 也需要重新读取整个表

标签: mysql amazon-web-services amazon-rds


【解决方案1】:

尝试使用EXPLAIN 分析您的SELECT DISTINCT 查询。我敢打赌它将包括“使用临时”和/或“使用文件排序”。如果结果集足够大,这些查询将使用临时磁盘空间。但运行这些查询的频率越高,它使用的磁盘空间就越多。

如果行已经不同,我不知道你为什么使用SELECT DISTINCT *。这可能会导致不必要地使用临时表。

理想情况下,您的查询应该是:

SELECT *
FROM    owl
ORDER BY
        name
LIMIT 10;

确保name 列上有索引,因此它可以通过按名称按索引顺序读取行来跳过“使用文件排序”。

为什么不使用完整配置的 IOPS?我猜是因为 MySQL 受到构建临时表的代码的限制。它不能足够快地填充临时表以使大量 IOPS 饱和。也许如果您要在许多线程中同时运行此查询,它会。但也许不是。 IMO,预置的 IOPS 几乎是一个骗局。

【讨论】:

  • 我使用SELECT DISTINCT * 只是作为MYSQL 中的一个练习:) 我没有建立任何索引,这有助于避免重新读取整个数据库吗?这些临时表可以有多大 - 可用存储空间指标已经减少了 420GB - 对于其中包含 1.1TB 数据的表来说有点过分......
  • 临时表可以与查询结果集一样大,对于每个查询。查询完成时会清理它,但如果您倾向于一次运行多个查询以处理并发请求,它可能会很快耗尽可用空间。
  • 对于ORDER BY name LIMIT 100 的情况,请记住临时表需要与结果 LIMIT 之前一样大。由于查询需要对所有数据进行排序才能确定返回哪 100 行。
  • 我真的认为在这种情况下学习如何使用索引会对你有所帮助。你可能会喜欢我的演示文稿How to Design Indexes, Reallyvideo
  • 不幸的是,临时表的大小在 MySQL 中不可用。我在 2014 年提交了一个关于此的错误:bugs.mysql.com/bug.php?id=74484
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-12-03
  • 2013-07-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多