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

时间:2014-06-06 10:21:14

标签: mysql optimization query-optimization

当我在phpMyAdmin中运行查询时,它最多返回0.3秒内的结果。通常0.19s ish是正常的。看起来查询使用的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是玩家拥有的已知单位列表

2 个答案:

答案 0 :(得分:1)

首先,您的2个子查询非常相似。它们可能都返回相同的行(因此全部为union),但是您使用GROUP_BY来消除其中一个重复行。

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

最后,您使用的是field IN (sub query)类型语法,这种语法在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会删除这些行。因为你没有总结或计算行数,所以这不重要。

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

答案 1 :(得分:0)

请注意,您的第一个内部子选择是另一个的完美子集。考虑到这一点,为什么首先需要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
) AS results
WHERE unitId NOT IN (SELECT unitId FROM player_units WHERE playerId = 123)
GROUP BY unitName

然后,当您GROUP BY unitName时,您应该在外部SELECT的其他字段上使用某些聚合函数,例如sum()count()avg()或相似 - 否则根本就没有意义。

修复这些问题后,运行包含在EXPLAIN ANALYZE VERBOSE中的查询,并确保查询使用索引。如果它使用顺序扫描或文件排序,请确保添加缺少的索引,很可能是多列索引。