使用 MySQL 时 SUB QUERY、LIMIT 和 ORDER BY 的性能注意事项
Performance considerations for SUB QUERY, LIMIT and ORDER BY when using MySQL
Objective:Select 10 名用户正在进行的游戏少于 5。
方法 1:在 MySQL
范围内完成所有操作
Select 个用户(LIMIT 10)并为每个用户检查 n_ongoingGames
方法二:SQL + 结果集分析
获取 30 个用户的记录。
对于每条记录,再次转到数据库并检查 n_ongoingGames。假设每个用户 (n_ongoingGames > 5) 的概率为 50%,我们需要检查 20 条记录。
-- Approach 1: Do everything within MySQL
-- --------------------------------------
SELECT u.*
FROM USERS u
WHERE
( -- n_ongoingGames for user < 5
(
SELECT COUNT(gameID) AS n_ongoingGames
FROM games g
WHERE
(g.player1_userID = u.userID) OR
(g.player2_userID = u.userID) OR
(g.player3_userID = u.userID) OR
...
...
...
(g.player10_userID = u.userID)
) < 5
) AND
(u.cash_balance > 500)
ORDER BY u.user_rating
LIMIT 10;
-- Approach 2: SQL + result-set analysis
-- -------------------------------------
SELECT u.*
FROM USERS u
WHERE
(u.cash_balance > 500)
ORDER BY u.user_rating
LIMIT 30;
问题:
- 在方法 1 中,MySQL 是否为用户 table 中的每个用户检查(n_ongoingGames for user < 5)?
- 哪种方法更快?
- 如果只需要 1 个用户而不是 10 个(LIMIT 1),这会有所不同吗(哪种方法更快)
谢谢。
我建议如下,假设用户不能同时是玩家:
SELECT u.*
FROM USERS u
WHERE u.cash_balance > 500 AND
((SELECT COUNT(*) AS n_ongoingGames
FROM games g
WHERE g.player1_userID = u.userID
) +
(SELECT COUNT(*) AS n_ongoingGames
FROM games g
WHERE g.player2_userID = u.userID
)
) < 5
ORDER BY u.user_rating
LIMIT 10;
具有以下索引:games(player1_userID)
和 games(player2_userID)
。您还需要一个用户索引;一种可能性是 users(user_rating, cash_balance)
,但我不认为 MySQL 足够聪明,可以使用索引按顺序扫描 table。您可能不得不接受 users(cash_balance)
.
games()
上的索引意味着索引中可以满足计数。这应该是一种特别快速的计数。如果您删除现金余额条件,那么 users(user_rating)
上的索引应该会使查询非常快。
Objective:Select 10 名用户正在进行的游戏少于 5。
方法 1:在 MySQL
范围内完成所有操作Select 个用户(LIMIT 10)并为每个用户检查 n_ongoingGames
方法二:SQL + 结果集分析
获取 30 个用户的记录。 对于每条记录,再次转到数据库并检查 n_ongoingGames。假设每个用户 (n_ongoingGames > 5) 的概率为 50%,我们需要检查 20 条记录。
-- Approach 1: Do everything within MySQL
-- --------------------------------------
SELECT u.*
FROM USERS u
WHERE
( -- n_ongoingGames for user < 5
(
SELECT COUNT(gameID) AS n_ongoingGames
FROM games g
WHERE
(g.player1_userID = u.userID) OR
(g.player2_userID = u.userID) OR
(g.player3_userID = u.userID) OR
...
...
...
(g.player10_userID = u.userID)
) < 5
) AND
(u.cash_balance > 500)
ORDER BY u.user_rating
LIMIT 10;
-- Approach 2: SQL + result-set analysis
-- -------------------------------------
SELECT u.*
FROM USERS u
WHERE
(u.cash_balance > 500)
ORDER BY u.user_rating
LIMIT 30;
问题:
- 在方法 1 中,MySQL 是否为用户 table 中的每个用户检查(n_ongoingGames for user < 5)?
- 哪种方法更快?
- 如果只需要 1 个用户而不是 10 个(LIMIT 1),这会有所不同吗(哪种方法更快)
谢谢。
我建议如下,假设用户不能同时是玩家:
SELECT u.*
FROM USERS u
WHERE u.cash_balance > 500 AND
((SELECT COUNT(*) AS n_ongoingGames
FROM games g
WHERE g.player1_userID = u.userID
) +
(SELECT COUNT(*) AS n_ongoingGames
FROM games g
WHERE g.player2_userID = u.userID
)
) < 5
ORDER BY u.user_rating
LIMIT 10;
具有以下索引:games(player1_userID)
和 games(player2_userID)
。您还需要一个用户索引;一种可能性是 users(user_rating, cash_balance)
,但我不认为 MySQL 足够聪明,可以使用索引按顺序扫描 table。您可能不得不接受 users(cash_balance)
.
games()
上的索引意味着索引中可以满足计数。这应该是一种特别快速的计数。如果您删除现金余额条件,那么 users(user_rating)
上的索引应该会使查询非常快。