问题描述
我想弄清楚为什么一个带有 LIMIT 1
子句的简单选择(不可否认,在一个有很多行和索引的非常臃肿的表上)有时需要 30+ 秒(甚至 2 分钟,有时) 在 AWS RDS aurora 实例上执行。这是在编写器实例上。
它似乎发生在来自客户端的第一个查询时,仅在查看数十万行的特定选择上发生,并且只是有时。
查询格式为:
SELECT some_table.col1,some_table.col2,some_table.col3,some_table.col4,MAX(some_table.col2) AS SomeValue
FROM some_table
WHERE some_table.col3=123456 LIMIT 1;
并“解释”输出:
+----+-------------+---------------+------+---------------+---------+---------+-------+--------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------------+------+---------------+---------+---------+-------+--------+-------+
| 1 | SIMPLE | some_table | ref | col1 | col1 | 4 | const | 268202 | NULL |
+----+-------------+---------------+------+---------------+---------+---------+-------+--------+-------+
我设法重现了该问题并在 PHPMyAdmin 中捕获了查询的配置文件。 PHPMyAdmin 将查询记录为执行需要 30.1 秒,但分析器显示执行本身不到一秒:
所以看起来执行本身并没有花很多时间;什么可能导致此延迟问题?我还在 RDS Performance Insights 中发现了相同的查询:
这似乎发生在一系列相同或相似查询中的第一个查询中。会不会是缓存问题?我尝试运行 RESET QUERY CACHE;
以尝试重现延迟,但没有成功。如果有帮助,我们很乐意提供有关基础设施的更多信息。
更多信息
SHOW VARIABLES LIKE 'query_cache%';
SHOW GLOBAL STATUS LIKE 'Qc%';
检查和发送的行(来自 Performance Insights):
SHOW CREATE TABLE
输出:
CREATE TABLE `some_table` (
`col1` int(10) unsigned NOT NULL AUTO_INCREMENT,`col2` int(10) unsigned NOT NULL DEFAULT '0',`col3` int(10) unsigned NOT NULL DEFAULT '0',`col4` int(10) unsigned NOT NULL DEFAULT '0',`col5` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,`col6` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',`col7` int(10) unsigned NOT NULL DEFAULT '0',PRIMARY KEY (`col1`),KEY `col2` (`col2`),KEY `col3` (`col3`),KEY `col4` (`col4`),KEY `col6` (`col6`),KEY `col7` (`col7`)
) ENGINE=InnoDB AUTO_INCREMENT=123456789 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
解决方法
可能的解释是:
- 查询被延迟执行,因为它正在等待锁定。即使像
SELECT
这样的只读查询也可能需要等待元数据锁定。 - 查询必须检查数十万行,并且从存储中读取这些行需要时间。 Aurora 应该具有快速存储,但它不可能是零成本。
- Aurora 实例上的系统负载过高,因为它正在与您正在运行的其他查询竞争。
- Aurora 实例上的系统负载过高,因为主机由其他亚马逊客户拥有的其他 Aurora 实例共享。这种情况有时被称为“吵闹的邻居”,实际上您无能为力。亚马逊会自动为不同客户在同一硬件上托管虚拟机。
- 将结果集传输到客户端需要很长时间。由于您使用
LIMIT 1
,因此该单行必须很大,需要 30 秒,否则您的客户端必须使用非常慢的网络。
第一次运行查询时,查询缓存不相关。随后执行相同的查询会更快,直到从缓存中驱逐结果之后的某个时间,或者如果该表中的任何数据被更新,这会强制从查询缓存中驱逐针对该表的所有查询的结果.
,在这种情况下,您对 LIMIT
函数的理解似乎不太正确。
如果您要运行像 SELECT * FROM tablea LIMIT 1;
这样的简单函数,那么数据库会向您显示它遇到的第一行并在那里终止,让您快速返回。
但是在上面的示例中,您同时拥有聚合函数和 WHERE 子句。 因此,为了让您的数据库返回第一行,它必须首先返回整个数据集,然后计算出第一行是什么。
您可以在之前的问题中阅读有关此内容的更多信息; https://dba.stackexchange.com/a/62444
如果您要在最后不带 limit 1
的情况下运行相同的查询,您可能会发现返回结果所需的时间大致相同。
正如您在评论中提到的,最好查看架构并找出如何修改此查询以提高效率。