我在 mysql 中的高分列表存在很大的性能问题。基本上我们有用户,我们有游戏,用户可以玩游戏并在这些游戏中获得分数。我们希望有一个可通过自定义过滤器浏览的顶级列表,因此用户可以显示所有分数或按游戏 X 或其他因素过滤它。不幸的是,当前的列表完全没有性能。它目前有 50 万个数据集,而我们的选择查询需要 20 多秒,这太长了。
这是当前的表结构:
CREATE TABLE IF NOT EXISTS `score` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT, # just an AI id
`userid` int(10) unsigned NOT NULL, # foreign key for `users` table
`gameid` varchar(255) NOT NULL, # foreign key for `games` table
`date` date NOT NULL, # date of score
`score` smallint(4) unsigned NOT NULL, # total score
`score_level1` smallint(4) unsigned NOT NULL, # score in level 1
`score_level2` smallint(4) unsigned NOT NULL, # score in level 2
`score_level3` smallint(4) unsigned NOT NULL, # score in level 3
`score_level4` smallint(4) unsigned NOT NULL, # score in level 4
`score_level5` smallint(4) unsigned NOT NULL, # score in level 5
`times_played` smallint(4) unsigned NOT NULL, # this is the n-th time the user plays this game (I want to know which score was his 1st try, which score was his 5th try etc)
PRIMARY KEY (`id`),
UNIQUE KEY `user_game_date` (`userid`,`gameid`,`date`), # we save only the latest score per user per game per day (this unique index is for "replace into")
UNIQUE KEY `user_game_times` (`userid`,`gameid`,`times_played`), # obviously a user cant play the game multiple times for the 3rd time
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ADD CONSTRAINT `score_ibfk_3` FOREIGN KEY (`userid`) REFERENCES `users` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
ADD CONSTRAINT `score_ibfk_4` FOREIGN KEY (`gameid`) REFERENCES `games` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION;
我们的选择查询如下所示:
SELECT `users`.`name` AS `username`, # we want to display who got a certain score
`users`.`country` AS `country`, # we want to display country of a user. also users can filter the scorelist to display "italy only" for example
`games`.`name` AS `gamename`, # show which game score is for
`score`.`score` AS `score`, # the score, obviously
`score`.`score_level1` AS `score1`, # display which score the user got in every level
`score`.`score_level2` AS `score2`,
`score`.`score_level3` AS `score3`,
`score`.`score_level4` AS `score4`,
`score`.`score_level5` AS `score5`,
`score`.`times_played` AS `times_played`, # show how many attempts a user needed to achieve this very score
FROM `score`
INNER JOIN ( # this inner query is to make that we only display the latest score per user per game (yes, we do NOT want to display the highest score, but the latest). we need to keep the old data in the database though to display the score progress to the user on a different page
SELECT `userid`,
`gameid`,
MAX(`date`) AS `date`
FROM `score`
GROUP BY `userid`, `gameid`
ORDER BY `score` DESC
) `temp` ON `score`.`userid` = `temp`.`userid` AND `score`.`gameid` = `temp`.`gameid` AND `score`.`date` = `temp`.`date`
INNER JOIN `users` ON (`users`.`id` = `score`.`userid`)
INNER JOIN `games` ON (`games`.`id` = `score`.`gameid`)
$filter # php variable to filter. this can be empty or contain something like "where `gameid` = 4" or "where `users`.`country` = 'it'"
LIMIT :limit,:limit2 # this is done by paging function. we display 50 entries per page. (e.g. "0, 50" for page 1, "50, 50" for page 2 etc)
解释选择查询:
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 585106
1 PRIMARY users eq_ref PRIMARY PRIMARY 4 temp.userid 1
1 PRIMARY games eq_ref PRIMARY PRIMARY 767 temp.gameid 1
1 PRIMARY score eq_ref user_game_date,user_game_times,games user_game_date 774 temp.userid,games.id,temp.date 1 Using where
2 DERIVED score index NULL user_game_date 774 NULL 608211 Using temporary; Using filesort
选择查询的分析(限制为“0,50”并且$filter
为空):
Status Duration
starting 0.000018
Waiting for query cache lock 0.000003
checking query cache for query 0.000063
checking permissions 0.000004
checking permissions 0.000002
checking permissions 0.000002
checking permissions 0.000003
Opening tables 0.000023
System lock 0.000035
optimizing 0.000005
statistics 0.000010
preparing 0.000008
Creating tmp table 0.000009
Sorting for group 0.000004
executing 0.000002
Copying to tmp table 0.194463
converting HEAP to MyISAM 0.042438
Copying to tmp table on disk 1.630471
Sorting result 16.097164
Sending data 0.061229
converting HEAP to MyISAM 0.805552
Sending data 7.414902
removing tmp table 1.944732
Sending data 0.000023
Waiting for query cache lock 0.000003
Sending data 0.000007
init 0.028244
optimizing 0.000026
statistics 0.000037
preparing 0.000024
executing 0.000004
Sending data 0.000539
end 0.000008
query end 0.000005
closing tables 0.000002
removing tmp table 2.130069
closing tables 0.000028
freeing items 0.000684
logging slow query 0.000005
logging slow query 0.000004
cleaning up 0.000005
那么我该如何改进以获得体面的表现呢?您对更改选择查询或表结构有什么建议吗?我不介意对结构进行重大更改,只要它有助于提高性能