为什么 mysql 会在有效查询时休眠,而在无效请求时花费两倍的时间

Why mysql does sleep on valid query and takes twice as long on an invalid request

我正在挖掘一些关于 SQL 问题的东西,我看到 sleep(2) 花了 4s 例如在我看到它是一个旧的 CentOS 6 和一个旧的 MySQL

但我能够在本地重现这个简单的案例,并且我在 Ubuntu 20.04 和 mariadb 上有相同的行为:

MariaDB [tmp]> select * from users;
+----+----------+
| id | username |
+----+----------+
|  1 | admin    |
|  2 | jdoe     |
+----+----------+
2 rows in set (0.000 sec)

MariaDB [tmp]> select * from users where username = 'admin';
+----+----------+
| id | username |
+----+----------+
|  1 | admin    |
+----+----------+
1 row in set (0.000 sec)

MariaDB [tmp]> select * from users where username = 'admin' or sleep(2);
+----+----------+
| id | username |
+----+----------+
|  1 | admin    |
+----+----------+
1 row in set (2.006 sec)

MariaDB [tmp]> select * from users where username = 'admin' or sleep(2) limit 1;
+----+----------+
| id | username |
+----+----------+
|  1 | admin    |
+----+----------+
1 row in set (0.000 sec)

MariaDB [tmp]> select * from users where username = 'nonexistant' or sleep(2) limit 1;
Empty set (4.006 sec)

MariaDB [BofT]> delete from users where username = 'jdoe';
Query OK, 1 row affected (0.002 sec)

MariaDB [tmp]> select * from users where username = 'admin' or sleep(2);
+----+----------+
| id | username |
+----+----------+
|  1 | admin    |
+----+----------+
1 row in set (0.000 sec)

MariaDB [tmp]> select * from users where username = 'nonexistant' or sleep(2) limit 1;
Empty set (2.001 sec)

我只用这个创建了基础 :

CREATE TABLE IF NOT EXISTS users (
  id INT PRIMARY KEY NOT NULL,
  username VARCHAR(100)
);

INSERT INTO users (id, username) VALUES (1, 'admin');
INSERT INTO users (id, username) VALUES (2, 'jdoe');

例如为什么第三个有效的请求select * from users where username = 'admin' or sleep(2);仍然需要两秒?

除非可以使用索引优化 WHERE 条件,否则数据库将按顺序扫描 table,评估每一行的条件。根据您的结果,username 列未编入索引,因此我们可以忽略该优化。

OR 运算符执行 short-circuiting,因此 username = "something" OR sleep(2) 只会在用户名不等于 something 时休眠。如果您改写 WHERE sleep(2) OR username = "something",它会为它测试的每一行休眠。

此外,如果您有 LIMIT 子句,一旦找到许多匹配行,它将停止扫描。

当您将所有这些放在一起时,这意味着查询将在 table 中扫描的每一行在找到具有匹配用户名的行之前休眠 2 秒。如果没有匹配的用户名,它将在 table.

中的每一行休眠 2 秒