为什么 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 秒
我正在挖掘一些关于 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 秒