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,直到找到匹配的行。我的版本会

  1. 进行两次索引查找;
  2. 获取一行的其他列。

无论 USERID 有多少行,速度都是一样的,很快。

(建议切换到 InnoDB。)