SELECT DISTINCT 语句与 RDS 的奇怪行为

Weird behavior of SELECT DISTINCT statement with RDS

我创建了一个 2TB MySQL RDS,并用 2 tables 填充了它,总计 1.5TB:

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

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

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

SELECT  DISTINCT *
FROM    owl
ORDER BY
        name
LIMIT 10;

令我惊讶的是,此查询在过去 2 小时内 运行... 更令人惊讶的是,“免费存储 Space”AWS 指标开始以 2.2GB/分钟的速度下降:

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

READ 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/ 找到了一个解释——“大多数时候需要排序阶段的查询需要依赖一个临时的 table。例如,当你使用GROUP BY, ORDER BY or DISTINCT. 此类查询分两个阶段执行:第一个是收集数据并将它们放入临时table,第二个是对临时table执行排序”因此,即使使用 ORDER BY 的常规 SELECT 也需要重新读取整个 table

Q1) 大量写入是由为查询创建的临时 table 引起的,它们可以达到原始 table.

的 100%

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

尝试使用 EXPLAIN 来分析您的 SELECT DISTINCT 查询。我敢打赌它会包括“使用临时”and/or“使用文件排序”。如果结果集足够大,这些查询将使用临时磁盘 space。但是您 运行 这些查询越频繁,它使用的磁盘 space 就越多。

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

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

SELECT *
FROM    owl
ORDER BY
        name
LIMIT 10;

确保 name 列上有一个索引,这样它就可以通过按名称读取索引顺序中的行来跳过“使用文件排序”。

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