某些参数的 SQL 查询非常慢(MySQL)

Posted

技术标签:

【中文标题】某些参数的 SQL 查询非常慢(MySQL)【英文标题】:SQL query is very slow for certain parameters (MySQL) 【发布时间】:2019-12-02 16:10:35 【问题描述】:

我正在创建一个 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 000 个匹配行的用户,它将在不到 100 毫秒的时间内返回结果。但是,我后来添加了新用户,这些用户的匹配行大约不到 100 行,而且查询速度对他们来说非常慢,每次大约需要 20-30 秒。

我很困惑为什么查询在应该返回少量行的情况下会慢得多,而在返回大量行时却非常快,尤其是因为我有ORDER BY

我已经阅读过有关参数嗅探的信息,但据我所知,那是 SQL 服务器的东西,我正在使用 MySQL

编辑

这是SHOW CREATE 声明:

CREATE TABLEgames( 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 partitions type possible_keys key key_len ref rows filtered Extra 1 SIMPLE games NULL index NULL PRIMARY 4 NULL 1 19.00 Using where

我尝试了准备好的语句,但仍然得到相同的结果。

对不起,格式不好,我还是菜鸟。

【问题讨论】:

查看准备好的和绑定的查询 请edit您的问题向我们展示您的表的定义;即 SHOW CREATE TABLE 的输出。我们可能会建议您添加索引。而且,请澄清:您向我们展示的查询仅返回一行。你说它可以返回 30K 行;是来自不同的查询吗? 那些索引不会帮助查询任何内容。 【参考方案1】:

您需要使用EXPLAIN 来分析查询的性能。

 EXPLAIN SELECT * FROM $TABLE_GAMES WHERE 
  ($GAME_RECEIVERID = '$userId'OR $GAME_OTHERID = '$userId') 
   ORDER BY $GAME_ID LIMIT 1"

EXPLAIN 将提供有关带有执行计划的选择查询的信息。 它是识别查询速度慢的好工具。根据获得的信息,您可以为WHERE 子句中使用的列创建Indexes

CREATE INDEX index_name ON table_name (column_list)

这肯定会提高查​​询的性能。

【讨论】:

唉,EXPLAIN 说的是正在发生的事情,而不是如何让它变得更好。【参考方案2】:

您的查询速度很慢,因为它无法足够快地找到匹配的记录。对于很多行匹配的用户,在所有其他条件相同的情况下,找到要返回的记录的机会要高得多。

$GAME_RECEIVERID$GAME_OTHERID 不是索引的一部分时会出现这种行为,提示MySQL 由于排序而使用$GAME_ID 上的索引。然而,由于新玩家没有玩过早期的游戏,实际上有数百万行不匹配,但仍然需要检查。

不幸的是,随着数据库的增长,即使对老用户来说,这种情况也会变得更糟。理想情况下,您将在 $GAME_RECEIVERID$GAME_OTHERID 上添加索引 - 类似于:

ALTER TABLE games
ADD INDEX receiver (ReceiverID),
ADD INDEX other (OtherID)

PS:修改一个 1700 万行的表需要一段时间,所以如果在生产环境中使用,请确保在维护窗口或类似情况下进行。

【讨论】:

您好,谢谢您的帮助!据我所知,向某些列添加索引会使数据库以一种能够更快获得结果的方式对数据进行排序。理想情况下,我应该索引我用作搜索参数的每一列,对吧?除了增加 db 文件大小之外,添加索引是否会产生任何负面影响? 它还增加了插入时间(尽管通常可以忽略不计),因为必须重新计算索引,因为每个新记录都按照每个索引放在其设计的位置。根据您搜索的确切内容和添加的索引,它们也有可能并未完全发挥其潜力,或者相反,有些可能无用。如需更多阅读,如果您有兴趣,请here's a nice article 了解该主题。 嘿,刚刚添加了这些索引,现在所有查询都超级快,太神奇了!有趣的是,只用了 2 分钟 :) 很高兴听到它有帮助:) @asd123 请张贴 A)您查询的当前解释和 B)显示创建表游戏;进行分析。【参考方案3】:

这是插值后的查询吗?也就是说,这是 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)

您的查询版本正在扫描表,直到找到匹配的行。我的版本会

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

无论 USERID 有多少行,它都将是相同的、快速的、速度。

(建议切换到 InnoDB。)

【讨论】:

以上是关于某些参数的 SQL 查询非常慢(MySQL)的主要内容,如果未能解决你的问题,请参考以下文章

MySQL5.6 怎样优化慢查询的SQL语句 -- 慢日志介绍

mysql如何找出慢sql

如何查找MySQL中查询慢的SQL语句

MYSQL高级:慢查询日志

mysql数据库查询好慢怎么解决

Mysql慢查询