0

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

(明らかに悪名高い Using temporary と Using 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 秒に短縮されます。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,Index_3_points'  'Index_3_points'      '4'       'earzsql.se.id_sessions' '35'    'Using index'

誰かがさらに最適化のトリックを知っているなら、それを聞いてみたい.

4

1 に答える 1

0

おそらく、上位50のスコアはそれほど頻繁には変更されませんか?

したがって、クエリをTopScoreテーブルに実行し、インデックスを作成します。ユーザーのスコアが変更された場合は、ハイスコアテーブルと照合し、ユーザーのスコアが50番目よりも優れている場合にのみTopScoreテーブルを更新します。

また、頻繁に更新されるテーブルに多くのインデックスを追加すると、そのテーブルのパフォーマンスに悪影響を与える可能性があることもお勧めします。

于 2012-07-17T13:18:34.443 に答える