【问题标题】:How can I make this multiple join MySQL query cleaner/with better performance?如何使这个多重连接 MySQL 查询更干净/性能更好?
【发布时间】:2013-01-05 05:48:48
【问题描述】:

我正在尝试为我正在开发的网站构建搜索查询,以构建最近/在线玩家列表、他们正在玩或想玩的游戏、他们正在玩的平台,如果他们有麦克风、平台的玩家标签/图像以及他们想要玩的游戏模式。以下查询为我做这件事。但是,它非常缓慢。我已经为存活 1 分钟的数据做了一个缓存。

此外,users_games_modes ugm 加入了user_id,而不是game_id,就像我希望的那样。我不知道如何在加入之前不移动games_modes 的情况下使其与game_id 匹配。如果我先移动连接,我将如何连接来自ugm 连接的结果的数据? (我希望这是有道理的)

  • 使用ou1.game_id 修复了这个问题

我对 where 或 join on 语句中的每个项目都有索引。
ou1.stop 是通过 PDO 动态添加的
使用的语言 PHP 和 MySQL

SELECT
    ou1.id as oid,
    ou1.user_id,
    ou1.game_id as game_id,
    CONCAT(SUBSTRING(ou1.comment, 1, 7), '...') as comment,
    users_systems.mic,
    users_gamernames.user_name,
    games.game_name,
    ou1.start,
    ou1.stop,
    ou1.system_id as system_id,
    (select value from users_settings where users_settings.user_id = ou1.user_id and users_settings.setting = 'display_name') as display_name,
    CASE
        WHEN ou1.system_id = '1' THEN 'xbox_tile'
        WHEN ou1.system_id = '2' THEN 'ps3_tile'
        WHEN ou1.system_id = '3' THEN 'steam_tile'
    END AS platform,
    (select value from users_settings where users_settings.user_id = ou1.user_id and users_settings.setting = platform) as platform_tile,
    GROUP_CONCAT(DISTINCT gm.short_name ORDER BY gm.short_name SEPARATOR ', ') as modes
FROM online_users as ou1
LEFT JOIN online_users ou2 ON (ou1.user_id = ou2.user_id and ou1.id < ou2.id and ou1.stop < '2013-01-28 23:59:59')
LEFT JOIN users_gamernames ON (users_gamernames.user_id = ou1.user_id AND users_gamernames.system_id = ou1.system_id)
LEFT JOIN users_systems ON (ou1.user_id = users_systems.user_id AND ou1.system_id = users_systems.system_id)
LEFT JOIN users_games_modes ugm ON (ou1.user_id = ugm.user_id AND ugm.game_id = ou1.game_id)
LEFT JOIN games_modes gm ON (ugm.mode_id = gm.id AND ou1.game_id = ou1.game_id)
RIGHT JOIN games ON (ou1.game_id = games.id)
WHERE ou2.id IS NULL
AND ou1.id IS NOT NULL
AND ou1.stop<='2013-01-28 23:59:59'
GROUP BY ou1.system_id, ou1.user_id
ORDER BY ou1.stop ASC
LIMIT 50;

这是 sql-adminer 的 EXPLAIN 输出:

http://i.imgur.com/N1ykp.png

games_modes 索引:

http://i.imgur.com/m9DLF.png

在线用户指数:

http://i.imgur.com/jf0wz.png

users_games_modes 索引:

http://i.imgur.com/I0PUR.png

  • 编辑 1:删除了 2 个 JOIN,添加了一个 CASE 和一个子选择,从运行时中删除了一些内容,并使其更好地用于未来的更新。
    • 绝大多数运行时间似乎来自users_games_modes 连接。当我删除它时,它从 16 秒变为 0.32 秒。我在user_id 上有一个索引。 (我附上了users_games_modes的索引截图
  • 编辑 2:向 users_games_modes 添加了一个 game_id 列,将运行时间从 16 秒降低到 2.4 秒。
    • 更新了users_games_modes4的索引截图

【问题讨论】:

  • 我认为您应该看看如何提高查询的性能。向我们展示一些索引。
  • 我添加了 EXPLAIN 和 2 个索引

标签: mysql


【解决方案1】:
        SELECT 
            ou1.id as oid,
            ou1.user_id,
            ou1.comment,
            users_systems.mic,
            users_gamernames.user_name,
            games.game_name,
            ou1.start,
            ou1.stop,
            ou1.system_id as system_id,
                (select value from users_settings where users_settings.user_id = ou1.user_id and users_settings.setting = 'display_name') as display_name,
                CASE
                    WHEN ou1.system_id = '1' THEN 'xbox_tile'
                    WHEN ou1.system_id = '2' THEN 'ps3_tile'
                    WHEN ou1.system_id = '3' THEN 'steam_tile'
                END AS platform,
                (select value from users_settings where users_settings.user_id = ou1.user_id and users_settings.setting = platform) as platform_tile,
                ugmt.modes
        FROM online_users as ou1
        LEFT JOIN users_gamernames ON (users_gamernames.user_id = ou1.user_id AND users_gamernames.system_id = ou1.system_id)
        LEFT JOIN users_systems ON (ou1.user_id = users_systems.user_id AND ou1.system_id = users_systems.system_id)
        LEFT JOIN users_games_modes_temp ugmt ON (ou1.user_id = ugmt.user_id AND ugmt.game_id = ou1.game_id)
        RIGHT JOIN games ON (ou1.game_id = games.id)
        WHERE ou1.expired=0
        AND ou1.start<='2013-01-04 21:47'
        AND ou1.stop>='2013-01-04 21:47'
        GROUP BY ou1.id
        ORDER BY ou1.stop DESC
        LIMIT 50;

我删除了 users_games_modesgames_modes 上的 LEFT JOIN 和 GROUP_CONCAT,并更改了更新 users_games_modes 的工具中的代码,以便在更改时更新 users_games_modes_temp 中的条目。

这将整个查询从 16 秒更改为 0.002 秒。

我认为运行时的巨大变化是由于连接为每行查询中的所有用户为所有游戏拉取users_games_modes

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-01-13
    • 1970-01-01
    • 2014-11-30
    • 2014-01-27
    • 1970-01-01
    • 1970-01-01
    • 2010-12-28
    • 2015-04-06
    相关资源
    最近更新 更多