MySQL 查询消耗的 CPU 使用率是正常 CPU 使用率的 5-6 倍

标签 mysql optimization query-optimization

当我在 phpMyAdmin 中运行查询时,最多会在 0.3 秒内返回结果。通常 0.19 秒左右是正常的。该查询似乎没有使用太多 CPU,但我的托管提供商告诉我它消耗了相当多的 CPU。

我想知道 UNION ALL 是否是造成这种情况的罪魁祸首,或者只是一般查询的复杂性。任何见解将不胜感激。

这是查询:

SELECT unitId, unitName, createDate
FROM (
    (SELECT game_player.unitId as unitId, unit.name as unitName, game.createDate as createDate
        FROM  `game_player` 
        LEFT JOIN `game` ON game_player.gameId = game.id
        LEFT JOIN `unit` ON game_player.unitId = unit.id
        WHERE game_player.playerId = 123
        AND game.createDate > 1390953600000
        AND game_player.unitId NOT IN (SELECT unitId FROM unit_free))

    UNION ALL

    (SELECT game_player.unitId as unitId, unit.name as unitName, game.createDate as createDate
        FROM  `game_player` 
        LEFT JOIN `game` ON game_player.gameId = game.id
        LEFT JOIN `unit` ON game_player.unitId = unit.id
        WHERE game_player.playerId = 123
        AND game.type = '5')
)
AS results
WHERE unitId NOT IN (SELECT unitId FROM player_units WHERE playerId = 123)
GROUP BY unitName

game 是游戏列表
game_player 是特定游戏中的玩家列表
unit 是玩家可以使用的单位列表
unit_free 是玩家可以玩的免费单位列表
player_units 是玩家拥有的已知单位的列表

最佳答案

首先,您的两个子查询非常相似。它们可能都返回相同的行(因此并集所有行),但您可以使用 GROUP_BY 来消除其中一个重复项。

您的第一个子查询正在检查 WHERE 子句中游戏表 (createDate) 中的字段。为此,已经找到了很多匹配项,因此可以使用 INNER JOIN 而不是 LEFT OUTER JOIN。您的第二个子查询对类型字段执行相同的操作。

最后,您使用的是 field IN(子查询) 类型语法,该语法在 MySQL 中优化不佳。使用 LEFT OUTER JOIN 然后检查 NULL 可能会更快。

请注意,未定义为最终的unitName 返回哪个unitName 和createDate 值。可能是其中任何一个。

忽略最后一点(如果您可以定义所需的值,我可以尝试进一步的解决方案),那么我将按如下方式调整查询

SELECT unitId, unitName, createDate
FROM (
    (SELECT game_player.unitId as unitId, unit.name as unitName, game.createDate as createDate
        FROM  `game_player` 
        INNER JOIN `game` ON game_player.gameId = game.id AND game.createDate > 1390953600000
        LEFT OUTER JOIN `unit` ON game_player.unitId = unit.id
        LEFT OUTER JOIN unit_free ON game_player.unitId = unit_free.unitId
        LEFT OUTER JOIN player_units ON game_player.unitId = player_units.unitId AND playerId = game_player.playerId
        WHERE game_player.playerId = 123
        AND unit_free.unitId IS NULL
        AND player_units.unitId IS NULL)

    UNION ALL

    (SELECT game_player.unitId as unitId, unit.name as unitName, game.createDate as createDate
        FROM  `game_player` 
        INNER JOIN `game` ON game_player.gameId = game.id AND game.type = '5'
        LEFT OUTER JOIN `unit` ON game_player.unitId = unit.id
        LEFT OUTER JOIN player_units ON game_player.unitId = player_units.unitId AND playerId = game_player.playerId
        WHERE game_player.playerId = 123
        AND player_units.unitId IS NULL
        )
)
AS results
GROUP BY unitName

如果 game_player unitId 必须出现在单位表中,我也会将它们更改为内部联接。

根据 createDate 值的可能要求(即,如果返回多个,则使用哪一个),那么很可能将其减少为单个查询或一对联合查询并删除需要任何子查询。

编辑

根据您的评论,这可能可以简化为:-

SELECT unit.id as unitId, unit.name as unitName, MAX(game.createDate) as createDate
FROM  `game_player` 
INNER JOIN `game` ON game_player.gameId = game.id 
INNER JOIN `unit` ON game_player.unitId = unit.id
LEFT OUTER JOIN unit_free ON game_player.unitId = unit_free.unitId
LEFT OUTER JOIN player_units ON game_player.unitId = player_units.unitId AND playerId = game_player.playerId
WHERE game_player.playerId = 123
AND ((unit_free.unitId IS NULL
AND game.createDate > 1390953600000) 
OR AND game.type = '5')
AND player_units.unitId IS NULL)
GROUP BY unitName, unitId 

由于 game_player.unitId 将始终出现在 unit.id 中,我们可以使用 INNER JOIN,并且还直接使用单元表中的 id(这将与单元名称匹配 - 否则按单元名称分组可能会在 2 时导致问题)不同单位有相同的名称)。由于您只想要一个创建日期,因此可以使用 MAX() 来获取最新的日期。

虽然由于unit_free可能会返回多行,但group by将删除这些行。由于您没有对行进行求和或计数,因此这并不重要。

假设有适当的索引,这应该相当快。

关于MySQL 查询消耗的 CPU 使用率是正常 CPU 使用率的 5-6 倍,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24079508/

相关文章:

c++ - C++11 : how does it optimize? 循环中函数返回值的内存分配

SQLite3 性能与 BETWEEN 范围的 OR

针对最大值的 MySQL 查询优化

mysql - 优化GROUP BY&ORDER BY查询

mysql连接三个表,同一个表两次

php - Laravel 字符串到文件限制

mysql - 这是动态表吗?或者是什么

mysql - 优化包含重复子查询的 MySQL 查询

MySQL UNIQUE CONSTRAINT 在随后的 INSERT CREATE TABLE 中失败

mysql - 每次我登录时,总是出现 CANNOT POST/login