MySQL 查询返回无效行并且非常慢

Posted

技术标签:

【中文标题】MySQL 查询返回无效行并且非常慢【英文标题】:MySQL Query returning invalid rows and very slow 【发布时间】:2016-03-20 23:29:11 【问题描述】:

我正在编写一个查询来从另一个表中选择玩家禁令,但首先它非常慢,大约需要 7-14 秒,其次它返回无效行。

第一个查询如下:

SELECT *
FROM   sourcebans.sb_bans  
WHERE  removetype IS NULL
       AND removedon IS NULL
       AND reason NOT LIKE '%[FragSleuth] Duplicate account%'
       AND ip IN(SELECT DISTINCT ip
                 FROM   fragsleuth.history
                 WHERE  trackingid = "ad000c3803b48190aabf382e01b957c9")
       OR authid IN(SELECT DISTINCT steamid
                    FROM   fragsleuth.history
                    WHERE  trackingid = "ad000c3803b48190aabf382e01b957c9") 

第二个查询如下

SELECT * FROM `history` WHERE trackingid = "ad000c3803b48190aabf382e01b957c9"

还有几张截图来说明我的意思: First QuerySecond Query

在屏幕截图 1 中,您可以看到当我要求查询仅返回具有 NULL 的行时,它返回了一个 removeon 和 removetype 不为空的行。

我还担心在历史记录表中会出现 steamid 和 ip 列的重复条目,这可能会使查询变慢,有没有办法让查询只选择具有唯一 ip 或 steamid 的行追踪ID?

任何帮助将不胜感激。

谢谢

编辑:我的帮助让我不知所措,感谢@maraca、@Skorpioh 和@Adam Silenko,查询时间现在不到一秒!

【问题讨论】:

【参考方案1】:

and 具有更高的优先级 then or... 你需要在你的表上建立索引 np。如果您没有,请在 fragsleuth.history 中为 trackingid 字段添加索引

使用一个子查询你可能会做得更快,但我不确定这一点。

SELECT *
FROM   sourcebans.sb_bans  
WHERE  removetype IS NULL
AND removedon IS NULL
AND reason NOT LIKE '%[FragSleuth] Duplicate account%'
AND exists (
  SELECT 1 from fragsleuth.history
  WHERE  trackingid = "ad000c3803b48190aabf382e01b957c9"
  and (ip = sourcebans.ip or steamid = sourcebans.authid) )

【讨论】:

嗨,你能解释一下在 trackingid 上添加索引会做什么吗?谢谢 如果表历史记录很大(可能是)并且您对子查询中的列 trackingid 有条件,则此字段上的索引可以改进此子查询的搜索行【参考方案2】:

查询返回不是 NULL 的行,因为它被解释为 (... AND ... AND ... ) OR ... 而不是 ... AND ... AND ( ... OR ...)

所以你需要添加一个大括号,也不需要DISTINCT

SELECT *
FROM   sourcebans.sb_bans  
WHERE  removetype IS NULL
       AND removedon IS NULL
       AND reason NOT LIKE '%[FragSleuth] Duplicate account%'
       AND (ip IN(SELECT ip
                 FROM   fragsleuth.history
                 WHERE  trackingid = "ad000c3803b48190aabf382e01b957c9")
       OR authid IN(SELECT steamid
                    FROM   fragsleuth.history
                    WHERE  trackingid = "ad000c3803b48190aabf382e01b957c9"))

【讨论】:

谢谢,解决了!但是现在有没有办法让查询更快? @SM9 不客气。您可以分析查询并对重要列进行索引。或者,如果您不能这样做,您可以尝试在 where 中重新排序条件,这有时会有所帮助。 @SM9 。 . .您可以提出另一个关于加快查询速度的问题。【参考方案3】:

您在这里遇到了运算符优先级问题,这就是为什么它最终会产生 removetype/removedon 不为 null 的结果。

如果您检查http://dev.mysql.com/doc/refman/5.7/en/operator-precedence.html,您会发现 AND 的优先级高于 OR,这意味着您的查询将运行与“AND”运算符粘合在一起的所有谓词,然后才执行 OR,这意味着您将看到 authorid 的结果是一场比赛,其余的都不重要了。

如果我没记错的话,下面应该可以正常工作:

SELECT *
FROM   sourcebans.sb_bans  
WHERE  removetype IS NULL
       AND removedon IS NULL
       AND reason NOT LIKE '%[FragSleuth] Duplicate account%'
       AND
           (
                ip IN (SELECT DISTINCT ip
                       FROM   fragsleuth.history
                       WHERE  trackingid = "ad000c3803b48190aabf382e01b957c9")
                OR
                authid IN(SELECT DISTINCT steamid
                          FROM   fragsleuth.history
                          WHERE  trackingid = "ad000c3803b48190aabf382e01b957c9")
           )

关于速度改进,您应该首先为 removetype、removedon、ip 和 authid 列创建覆盖索引。这会有所帮助,但可能还不够,因为 LIKE 操作非常昂贵。

你应该做的最后一件事是检查你是否可以改变

reason NOT LIKE '%[FragSleuth] Duplicate account%'

进入别的东西。例如,您能否消除领先的 % 以便它至少可以进行更快的匹配?当然取决于这些列到底存储了什么。

【讨论】:

原因是这样附加的:\"[FragSleuth] Duplicate account (Name: %s) (AuthId: %s) (Reason: %s)\"" 所以我不确定是否有是更好的方法。 如果您要过滤的原因总是以“[FragSleuth] Duplicate account”开头,那么您可以通过将其更改为“reason NOT LIKE '[FragSleuth] Duplicate account%'”(注意前面的 % 被删除了)。这将大大加快 LIKE 操作。 啊,好吧,我会这样做,编辑:哇,我做到了,我也做到了@Adam Silenko 所说的,查询时间减少了 7 秒到 0.0397 秒,非常感谢 :)

以上是关于MySQL 查询返回无效行并且非常慢的主要内容,如果未能解决你的问题,请参考以下文章

非常慢的 MySQL COUNT DISTINCT 查询,即使有索引——如何优化?

在 MySQL 和 PHP 中使用多表和 LEFT JOIN 查询非常慢

MySQL的alter table查询非常慢

返回大约 12000 行后的 MySQL 慢查询

使用 sum 和 group by 选项的 Mysql 查询运行速度非常慢

mysql慢查询日志分析工具 mysqlsla(转)