mysql의 최고 점수 목록에 큰 성능 문제가 있습니다. 기본적으로 우리에게는 사용자가 있고, 우리는 게임을 가지고 있으며 사용자는 게임을하고 그 게임에서 점수를 얻을 수 있습니다. 사용자 정의 필터로 탐색 할 수있는 뚜껑 목록을 원하므로 사용자는 게임 X 나 다른 요인으로 모든 점수를 표시하거나 필터링 할 수 있습니다. 불행히도 현재 목록은 전혀 실행되지 않습니다. 그것은 현재 50 만개의 데이터 세트를 가지고 있으며, 우리의 선택 쿼리는 20 초 이상 걸리고 너무 길다.MySQL에서 scorelist 테이블을 만들려고했지만 성능이 좋지 않습니다.
이 현재 테이블 구조입니다 :
우리의 선택 쿼리는 다음과 같습니다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;
: 한도 (선택 쿼리의
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
프로파일 :
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)
이 선택 쿼리를 설명 "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
그럼 어떻게 성능을 향상시킬 수 있습니까? 테이블 구조에 대한 선택 쿼리 또는 변경 사항에 대한 제안 사항이 있습니까? 구조에 큰 변화가 없다면 성능에 도움이된다면
나는 실제로 SQL 주입에 신경 쓰고있다! '$ filter'에 대한 한 가지 예는'WHERE users.country = : country'와 : country가 나중에 바인딩 된 것입니다. 인덱스 설정에 대한 팁을 주셔서 감사합니다. 'ORDER BY' 행 ('score')에 대해서'WHERE'를 사용하거나'ORDER BY' 행에 대해서만 인덱스를 설정해야합니까? – user2015253
또한 subselect에 조인되지 않은 행을 포함 할 수 있으므로'$ filter'를 subselect에 넣을 수 없습니다. subselect 안에 모든 조인을 이동하는 것이 영리한 것인지 확실하지 않습니다. 내가 틀렸다면 알려줘! – user2015253