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 利用率保持在较低的个位数:
我有几个问题:
- 为什么 SELECT DISTINCT 语句会导致大量写入数据库?
- 为什么 SELECT DISTINCT 会尝试读取整个数据库,而不仅仅是前 10 行?
- 为什么 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 几乎是一个骗局。
我创建了一个 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 利用率保持在较低的个位数:
我有几个问题:
- 为什么 SELECT DISTINCT 语句会导致大量写入数据库?
- 为什么 SELECT DISTINCT 会尝试读取整个数据库,而不仅仅是前 10 行?
- 为什么 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 几乎是一个骗局。