A question about query performance in MySQL. I have a table (the largest I've ever dealt with) of 2.3 million records (and growing). The table is part of a database keeping track of users logging in and scoring points in kind of seperate, quiz-like, sessions. For the query at hand I need the 'highscore table' of all the sessions.
So, the points scored in a session are stored per question in order to analyse the progress of the user better. A session combines the total of a user's points, and a session is connected to a user.
At first the query executiontime ran towards 12 seconds (unacceptable) with the table and query data as follows under 'Original set'. Under 'Improved scores table' there is the altered situation with some optimization in the indexes. This results in a query execution time of about 2 seconds.
My Question is: Is there an additional way to optimize? Like I said, 2.3 million (and counting) is the largest table I've ever seen, so I'm not that experienced at this and optimization sooner results in seconds than tenths of a second improvement.
Original set
CREATE TABLE `players` (
`id_players` int(10) unsigned NOT NULL AUTO_INCREMENT,
`id_organisations` int(10) unsigned NOT NULL,
`player_name` varchar(45) NOT NULL,
`player_comments` text NOT NULL,
PRIMARY KEY (`id_players`),
KEY `FK_players_organisation` (`id_organisations`),
CONSTRAINT `FK_players_organisation` FOREIGN KEY (`id_organisations`) REFERENCES `organisations` (`id_organisations`)
) ENGINE=InnoDB AUTO_INCREMENT=9139 DEFAULT CHARSET=latin1
SELECT COUNT(*) FROM players => 9126
CREATE TABLE `scores` (
`id_scores` int(10) unsigned NOT NULL AUTO_INCREMENT,
`id_sessions` int(10) unsigned NOT NULL,
`id_levels` int(10) unsigned NOT NULL,
`id_categories` int(10) unsigned NOT NULL,
`score_points` int(10) unsigned NOT NULL,
`score_correct` tinyint(4) NOT NULL,
`score_submitted` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id_scores`),
KEY `FK_scores_sessions` (`id_sessions`),
KEY `FK_scores_levels` (`id_levels`),
KEY `FK_scores_categories` (`id_categories`),
KEY `Index_3_points` (`score_points`),
KEY `Index_4_submitted` (`score_submitted`)
) ENGINE=InnoDB AUTO_INCREMENT=2328510 DEFAULT CHARSET=latin1
SELECT COUNT(*) FROM scores => 2328469
CREATE TABLE `sessions` (
`id_sessions` int(10) unsigned NOT NULL AUTO_INCREMENT,
`id_players` int(10) unsigned NOT NULL,
`id_classes` int(11) DEFAULT NULL,
`session_start` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`session_grade` decimal(4,1) NOT NULL,
`session_ip` varchar(45) DEFAULT NULL,
PRIMARY KEY (`id_sessions`),
KEY `FK_sessions_players` (`id_players`),
KEY `FK_sessions_classes` (`id_classes`)
) ENGINE=InnoDB AUTO_INCREMENT=40800 DEFAULT CHARSET=latin1
SELECT COUNT(*) FROM sessions => 40788
The 'offending' query:
SELECT sum( s.score_points ) AS score_points, p.player_name
FROM scores s
INNER JOIN sessions se ON s.id_sessions = se.id_sessions
INNER JOIN players p ON se.id_players = p.id_players
GROUP BY se.id_sessions
ORDER BY score_points DESC
LIMIT 50;
Above query took about 12 seconds with said scores table. (below the EXPLAIN ouput)
id select_type table type possible_keys key key_len ref rows Extra
'1' 'SIMPLE' 'p' 'ALL' 'PRIMARY' NULL NULL NULL '9326' 'Using temporary; Using filesort'
'1' 'SIMPLE' 'se' 'ref' 'PRIMARY,FK_sessions_players' 'FK_sessions_players' '4' 'earzsql.p.id_players' '2' 'Using index'
'1' 'SIMPLE' 's' 'ref' 'FK_scores_sessions' 'FK_scores_sessions' '4' 'earzsql.se.id_sessions' '72' ''
(the apparently infamous Using temporary and Using filesort)
After some 'research' I've changed indexes (Index_3_points) in scores table resulting in a table like:
Improved scores table
CREATE TABLE `scores` (
`id_scores` int(10) unsigned NOT NULL AUTO_INCREMENT,
`id_sessions` int(10) unsigned NOT NULL,
`id_levels` int(10) unsigned NOT NULL,
`id_categories` int(10) unsigned NOT NULL,
`score_points` int(10) unsigned NOT NULL,
`score_correct` tinyint(4) NOT NULL,
`score_submitted` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id_scores`),
KEY `FK_scores_sessions` (`id_sessions`),
KEY `FK_scores_levels` (`id_levels`),
KEY `FK_scores_categories` (`id_categories`),
KEY `Index_4_submitted` (`score_submitted`),
KEY `Index_3_points` (`id_sessions`,`score_points`)
) ENGINE=InnoDB AUTO_INCREMENT=2328510 DEFAULT CHARSET=latin1
With above scores table the query execution time drops to about 2 seconds. Explain (below) has not really changed a lot though (at least, the infamous temporary and filesorts are still used)
id select_type table type possible_keys key key_len ref rows Extra
'1' 'SIMPLE' 'p' 'ALL' 'PRIMARY' NULL NULL NULL '9326' 'Using temporary; Using filesort'
'1' 'SIMPLE' 'se' 'ref' 'PRIMARY,FK_sessions_players' 'FK_sessions_players' '4' 'earzsql.p.id_players' '2' 'Using index'
'1' 'SIMPLE' 's' 'ref' 'FK_scores_sessions,Index_3_points' 'Index_3_points' '4' 'earzsql.se.id_sessions' '35' 'Using index'
I'd love to hear it if anyone knows further optimization tricks.