SQL 某些参数的查询非常慢 (MySQL)
SQL query is very slow for certain parameters (MySQL)
我正在制作一个 PHP
后端 API,它在 MySQL
数据库上执行查询。这是查询:
SELECT * FROM $TABLE_GAMES WHERE
($GAME_RECEIVERID = '$userId'OR $GAME_OTHERID = '$userId')
ORDER BY $GAME_ID LIMIT 1"
本质上,我将 $userId
作为参数传递,并获取具有最小 $GAME_ID
值的行,对于大约 30 table 中有 000 个匹配行。但是,我已经添加了新用户,大约有 <100 个匹配行,查询对他们来说非常慢,每次大约需要 20-30 秒。
我很困惑为什么查询在应该 return 少量行的情况下如此慢,而在 return 大量行时却非常快特别是因为我有 ORDER BY
.
我读过有关参数嗅探的内容,但据我所知,那是 SQL
服务器的事情,我正在使用 MySQL
.
编辑
这里是 SHOW CREATE
语句:
CREATE TABLE
游戏(
IDint(11) NOT NULL AUTO_INCREMENT,
SenderIDint(11) NOT NULL,
ReceiverIDint(11) NOT NULL,
OtherIDint(11) NOT NULL,
Timestamptimestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (
ID)
) ENGINE=MyISAM AUTO_INCREMENT=17275279 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
这是EXPLAIN
的输出
+----+-------------+-------+------+---------------+------+---------+-----+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | extra |
+----+-------------+-------+------+---------------+------+---------+-----+------+-------+
| 1 | SIMPLE | games | NULL | index | NULL | PRIMARY | 4 | NULL | 1 |
+----+-------------+-------+------+---------------+------+---------+-----+------+-------+
id select_type table 分区类型 possible_keys key key_len ref rows filtered Extra<br>
1 SIMPLE games NULL 索引 NULL PRIMARY 4 NULL 1 19.00 使用 where
我尝试了预处理语句,但仍然得到相同的结果。
抱歉格式不好,我还是菜鸟。
您需要使用EXPLAIN
来分析查询的性能。
即
EXPLAIN SELECT * FROM $TABLE_GAMES WHERE
($GAME_RECEIVERID = '$userId'OR $GAME_OTHERID = '$userId')
ORDER BY $GAME_ID LIMIT 1"
EXPLAIN
将提供有关 select 查询和执行计划的信息。
它是识别查询缓慢的好工具。根据获得的信息,您可以为 WHERE
子句中使用的列创建 Indexes
。
CREATE INDEX index_name ON table_name (column_list)
这肯定会提高查询的性能。
您的查询速度很慢,因为它找不到足够快的匹配记录。对于很多行匹配的用户,找到 return 的记录的机会要高得多,所有其他条件都相同。
当 $GAME_RECEIVERID
和 $GAME_OTHERID
不是索引的一部分时会出现该行为,提示 MySQL 由于排序原因使用 $GAME_ID
上的索引。然而,由于新玩家没有玩过早期的游戏,实际上有数百万行不匹配,但仍然需要检查。
不幸的是,随着数据库的增长,即使对于老用户来说,这也必然会变得更糟。理想情况下,您将在 $GAME_RECEIVERID
和 $GAME_OTHERID
上添加索引 - 例如:
ALTER TABLE games
ADD INDEX receiver (ReceiverID),
ADD INDEX other (OtherID)
PS:更改 1700 万行 table 需要一段时间,因此请确保在维护期间执行此操作 window 或类似情况(如果在生产中使用) .
这是插值后的查询吗?也就是说,这就是MySQL会看到的吗?
SELECT * FROM GAMES
WHERE RECEIVERID = '123'
OR OTHERID = '123'
ORDER BY ID LIMIT 1
然后这将 运行 快,不管:
SELECT *
FROM GAMES
WHERE ID = LEAST(
( SELECT MIN(ID) FROM GAMES WHERE RECEIVERID = '123' ),
( SELECT MIN(ID) FROM GAMES WHERE OTHERID = '123' )
);
但是,您将需要这两个:
INDEX(RECEIVERID, ID),
INDEX(OTHERID, ID)
您的查询版本正在扫描 table,直到找到匹配的行。我的版本会
- 进行两次索引查找;
- 获取一行的其他列。
无论 USERID 有多少行,速度都是一样的,很快。
(建议切换到 InnoDB。)
我正在制作一个 PHP
后端 API,它在 MySQL
数据库上执行查询。这是查询:
SELECT * FROM $TABLE_GAMES WHERE
($GAME_RECEIVERID = '$userId'OR $GAME_OTHERID = '$userId')
ORDER BY $GAME_ID LIMIT 1"
本质上,我将 $userId
作为参数传递,并获取具有最小 $GAME_ID
值的行,对于大约 30 table 中有 000 个匹配行。但是,我已经添加了新用户,大约有 <100 个匹配行,查询对他们来说非常慢,每次大约需要 20-30 秒。
我很困惑为什么查询在应该 return 少量行的情况下如此慢,而在 return 大量行时却非常快特别是因为我有 ORDER BY
.
我读过有关参数嗅探的内容,但据我所知,那是 SQL
服务器的事情,我正在使用 MySQL
.
编辑
这里是 SHOW CREATE
语句:
CREATE TABLE
游戏(
IDint(11) NOT NULL AUTO_INCREMENT,
SenderIDint(11) NOT NULL,
ReceiverIDint(11) NOT NULL,
OtherIDint(11) NOT NULL,
Timestamptimestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (
ID)
) ENGINE=MyISAM AUTO_INCREMENT=17275279 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
这是EXPLAIN
+----+-------------+-------+------+---------------+------+---------+-----+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | extra |
+----+-------------+-------+------+---------------+------+---------+-----+------+-------+
| 1 | SIMPLE | games | NULL | index | NULL | PRIMARY | 4 | NULL | 1 |
+----+-------------+-------+------+---------------+------+---------+-----+------+-------+
id select_type table 分区类型 possible_keys key key_len ref rows filtered Extra<br>
1 SIMPLE games NULL 索引 NULL PRIMARY 4 NULL 1 19.00 使用 where
我尝试了预处理语句,但仍然得到相同的结果。
抱歉格式不好,我还是菜鸟。
您需要使用EXPLAIN
来分析查询的性能。
即
EXPLAIN SELECT * FROM $TABLE_GAMES WHERE
($GAME_RECEIVERID = '$userId'OR $GAME_OTHERID = '$userId')
ORDER BY $GAME_ID LIMIT 1"
EXPLAIN
将提供有关 select 查询和执行计划的信息。
它是识别查询缓慢的好工具。根据获得的信息,您可以为 WHERE
子句中使用的列创建 Indexes
。
CREATE INDEX index_name ON table_name (column_list)
这肯定会提高查询的性能。
您的查询速度很慢,因为它找不到足够快的匹配记录。对于很多行匹配的用户,找到 return 的记录的机会要高得多,所有其他条件都相同。
当 $GAME_RECEIVERID
和 $GAME_OTHERID
不是索引的一部分时会出现该行为,提示 MySQL 由于排序原因使用 $GAME_ID
上的索引。然而,由于新玩家没有玩过早期的游戏,实际上有数百万行不匹配,但仍然需要检查。
不幸的是,随着数据库的增长,即使对于老用户来说,这也必然会变得更糟。理想情况下,您将在 $GAME_RECEIVERID
和 $GAME_OTHERID
上添加索引 - 例如:
ALTER TABLE games
ADD INDEX receiver (ReceiverID),
ADD INDEX other (OtherID)
PS:更改 1700 万行 table 需要一段时间,因此请确保在维护期间执行此操作 window 或类似情况(如果在生产中使用) .
这是插值后的查询吗?也就是说,这就是MySQL会看到的吗?
SELECT * FROM GAMES
WHERE RECEIVERID = '123'
OR OTHERID = '123'
ORDER BY ID LIMIT 1
然后这将 运行 快,不管:
SELECT *
FROM GAMES
WHERE ID = LEAST(
( SELECT MIN(ID) FROM GAMES WHERE RECEIVERID = '123' ),
( SELECT MIN(ID) FROM GAMES WHERE OTHERID = '123' )
);
但是,您将需要这两个:
INDEX(RECEIVERID, ID),
INDEX(OTHERID, ID)
您的查询版本正在扫描 table,直到找到匹配的行。我的版本会
- 进行两次索引查找;
- 获取一行的其他列。
无论 USERID 有多少行,速度都是一样的,很快。
(建议切换到 InnoDB。)