优化MySQL索引和查询执行时间

时间:2012-07-17 12:41:15

标签: mysql sql query-optimization

关于MySQL中查询性能的问题。我有一张表(我曾经处理过的最大的一张)有230万条记录(并且还在增长)。该表是数据库的一部分,用于跟踪用户登录和以单独的类似测验的会话的方式评分点。对于手头的查询,我需要所有会话的“高分表”。

因此,每个问题都会存储会话中得分,以便更好地分析用户的进度。会话组合了用户点的总和,并且会话连接到用户。

首先,查询执行时间在“原始设置”下使用表和查询数据运行到12秒(不可接受)。在“改进的得分表”下,改变了情况,并在索引中进行了一些优化。这导致查询执行时间约为2秒。

我的问题是:还有其他优化方法吗?就像我说的那样,230万(并且还在计算)是我见过的最大的表,所以我不是那么经验丰富,优化的结果会在几秒钟内完成,而不是十分之一秒。

原始设置

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

'违规'查询:

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;

以上查询用上述得分表大约需要12秒。 (在EXPLAIN输出下面)

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'     ''

(显然臭名昭着使用临时和使用filesort)

经过一些'研究'之后,我在分数表中更改了索引(Index_3_points),得到了如下表格:

改进了得分表

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

使用以上分数表,查询执行时间下降到大约2秒。解释(下面)并没有真正改变很多(至少,臭名昭着的临时和filesorts仍在使用)

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'

如果有人知道进一步的优化技巧,我很乐意听到它。

1 个答案:

答案 0 :(得分:0)

据推测,前50名得分并不经常变化?

因此,将查询运行到TopScore表中,然后将其编入索引。当用户的分数发生变化时,请根据高分表对其进行检查,如果用户的分数优于50分,则仅更新TopScore表。

我还建议在经常更新的表中添加大量索引可能会对该表产生不利的性能影响。