2011-02-01 9 views
7

私はオンラインゲームのシンプルなハイスコアサービスを提供しており、予想以上に人気が高まっています。ハイスコ​​アは、以下に示すような単純なテーブルを持つMYSQLバックエンドを使用するWebサービスです。各高得点記録は、この表の行として格納されます。問題は、> 140k行以上で、特定の主要なクエリが非常に遅くなり、すぐに要求を処理するのが遅すぎることがわかります。高得点データベースのスケーリング

メインテーブルには次のようになります。

  • IDは、それぞれに固有のキーでは、レコード
  • ゲームが(現在は、常に「1」に等しいスコアを提出し、ゲームのID番号で得点します、すぐに
  • 名はそのプレイヤーの提出
  • playerIdの表示名です)しかしより多くのゲームをサポートする必要があります与えられたユーザ
  • スコアの一意のIDは、数値スコア表現EX 42035です
  • 時間は提出時間です
  • rankは、特定のゲームのスコア提出を一意にソートする大きな整数です。人々が特定の得点で結ぶのに共通するのは なので、その場合は、最初に提出した人物によって縛られます。したがって、このフィールドの値はほぼ等しい「* 100000000 +スコア(MAX_TIME - 時間)、」
 
+----------+---------------+------+-----+---------+----------------+ 
| Field | Type   | Null | Key | Default | Extra   | 
+----------+---------------+------+-----+---------+----------------+ 
| id  | int(11)  | NO | PRI | NULL | auto_increment | 
| game  | int(11)  | YES | MUL | NULL |    | 
| name  | varchar(100) | YES |  | NULL |    | 
| playerId | varchar(50) | YES |  | NULL |    | 
| score | int(11)  | YES |  | NULL |    | 
| time  | datetime  | YES |  | NULL |    | 
| rank  | decimal(50,0) | YES | MUL | NULL |    | 
+----------+---------------+------+-----+---------+----------------+ 

インデックスは次のようになります。

 
+-----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
| Table  | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | 
+-----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
| pozscores |   0 | PRIMARY |   1 | id   | A   |  138296 |  NULL | NULL |  | BTREE  |   | 
| pozscores |   0 | game  |   1 | game  | A   |  NULL |  NULL | NULL | YES | BTREE  |   | 
| pozscores |   0 | game  |   2 | rank  | A   |  NULL |  NULL | NULL | YES | BTREE  |   | 
| pozscores |   1 | rank  |   1 | rank  | A   |  138296 |  NULL | NULL | YES | BTREE  |   | 
+-----------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 

すると、ユーザーの要求ハイスコア、通常、彼ら「ランク降順リストでソート」の任意の点から約75の高得点を要求する。これらのリクエストは、通常、過去7日間の「全日制」またはスコア用です。

一般的なクエリは次のようになります。 "SELECT * FROM scoretable WHERE game=1 AND time>? ORDER BY rank DESC LIMIT 0, 75;" 0.00秒で実行されます。

ただし、リストの末尾にリクエストすると、 "SELECT * FROM scoretable WHERE game=1 AND time>? ORDER BY rank DESC LIMIT 10000, 75;"と0.06秒で実行されます。

"SELECT * FROM scoretable WHERE game=1 AND time>? ORDER BY rank DESC LIMIT 100000, 75;"であり、0.58秒で実行されます。

これは、毎日数千の新しいスコアが提出されるように、これはすぐに長くなりすぎるようです。

さらに、他の2つのタイプのクエリがあり、特定のプレーヤをID順に並べ替えることができます。 彼らは、次のようになります。

"SELECT * FROM scoretable WHERE game=1 AND time>? AND playerId=? ORDER BY rank DESC LIMIT 1"

"SELECT count(id) as count FROM scoretable WHERE game=1 AND time>? AND rank>[rank returned from above]"

私の質問が続きます。このスケーラブルなシステムにするために何ができますか?私は、すぐに数百万になるように成長する行の数を見ることができます。私はいくつかのスマートなインデックスを選ぶことが助けになることを期待していましたが、改善はほんのわずかでした。

更新:実測

 
mysql> explain SELECT * FROM scoretable WHERE game=1 AND time>0 ORDER BY rank DESC LIMIT 100000, 75; 
+----+-------------+-----------+-------+---------------+------+---------+------+--------+-------------+ 
| id | select_type | table  | type | possible_keys | key | key_len | ref | rows | Extra  | 
+----+-------------+-----------+-------+---------------+------+---------+------+--------+-------------+ 
| 1 | SIMPLE  | scoretable| range | game   | game | 5  | NULL | 138478 | Using where | 
+----+-------------+-----------+-------+---------------+------+---------+------+--------+-------------+ 

ソリューション:ここ は説明ラインです!

このスレッドのポインタのおかげで問題は解決しました。クラスタード・インデックスを行うことはまさに私が必要としていたものなので、クラスタード・インデックスをサポートするmysqlでInnoDBを使用するようにテーブルを変換しました。次に、idフィールドを削除し、プライマリキーを(ゲームASC、ランクDESC)に設定しました。現在、どのようなオフセットを使用しても、すべてのクエリは超高速で実行されます。説明では、追加の並べ替えが実行されていないことがわかり、すべてのトラフィックを簡単に処理できるように見えます。

+3

使用Mongo DB。それはWebスケールです。 – anon

+7

コメントを下落させることができないのは奇妙です(「Mongo DBを使用してください、それはWebスケールです」) – zerkms

+1

@ user509841:いくつか説明してください。 – zerkms

答えて

4

どのようにテイカーがないかを見て、私はそれを撃つでしょう。私はSQL Serverの背景ですが、同じ考え方が適用されます。

いくつかの一般的な観察:

  • ID列はかなり無意味です、あなたはについての私達に言っていない他のテーブル/クエリが存在しない限り、任意のインデックスに参加してはなりません。実際、それはあなたの最後の質問にある必要はありません。あなたはCOUNT(*)をすることができます。
  • クラスタ化インデックスは、最も一般的なクエリを対象とする必要があります。したがって、ゲームASC、時間DESC、およびランクDESCのクラスタ化インデックスはうまく機能します。時間によるソートDESCは、通常、最新のものに興味を持っているような履歴テーブルのための良いアイデアです。あなたは、他の方向でソートされたランクと別のインデックスを試すこともできますが、これはどれくらいのメリットがあるかわかりません。
  • SELECT *が必要ですか?より少ない列を選択できる場合は、SELECTおよびWHEREに必要なすべての列を含む索引を作成できます。

100万行はそれほど多くありません。 1,000,000行のサンプルデータを持つテーブルを作成し、1つのインデックス(ゲームASC、時間DESC、ランクDESC)でも、すべてのクエリは1秒未満で実行されました。

(私はplayerIdあるのかわからない部分だけ。クエリがうまくplayerIdが必要であると思わなかったことを行いました。おそらく、あなたはあなたのクラスタ化インデックスの末尾に追加することができます。)

+0

ありがとう!あなたが言うようにクラスタード・インデックスを作成するにはどうすればよいですか? –

+0

私はあなたがそれを理解しました=)あなたは時間を残して、あなたのクラスター化されたインデックスから選手を取り出したことが分かります。彼らはおそらく一日役に立つはずですが、私はそれを考えていますが、インサートがインデックスの最後に発生するため、時間の昇順が良いかもしれません。これについてはわかりません。あるいは、Mongo DBに切り替えることができます。これはweb scale =だからです) – anon

関連する問題