3

私は数百万行のデータの可能性がある mysql テーブルを持っています - いくつかの極端なケースでは最大 100mil です。このデータを頻繁にクエリするアプリケーションを開発し、それを最適化するためにできる限りのことを行いました - データの非常に小さなサブセット (場所に関連付けられている) のみを検索しているため、ほとんどの場合、非常に迅速に動作します。 .

テーブル構造:

CREATE TABLE `prism_actions` (
  `id` int(11) unsigned NOT NULL auto_increment,
  `action_time` timestamp NOT NULL default CURRENT_TIMESTAMP,
  `action_type` varchar(25) NOT NULL,
  `player` varchar(16) NOT NULL,
  `world` varchar(255) NOT NULL,
  `x` int(11) NOT NULL,
  `y` int(11) NOT NULL,
  `z` int(11) NOT NULL,
  `block_id` mediumint(5) unsigned NOT NULL,
  `block_subid` mediumint(5) unsigned NOT NULL,
  `data` varchar(255) NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `x` (`x`),
  KEY `action_type` (`action_type`),
  KEY `player` (`player`),
  KEY `block_id` (`block_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1;

WHERE ステートメントで最もよく使用するフィールドにいくつかの基本的なインデックスがあり、条件が 1 つだけのクエリで使用すると、非常に高速です。

これらのテストを実行しているテーブルの例には、2,200 万のレコードがあります。

例:

SELECT prism_actions.id FROM prism_actions WHERE prism_actions.action_type = 'block-break' LIMIT 1000;
1000 rows in set (0.00 sec)

SELECT prism_actions.id FROM prism_actions WHERE prism_actions.block_id = 2 LIMIT 1000;
1000 rows in set (0.01 sec)

私の問題は、クエリで使用する条件ごとに (ほとんどのクエリには通常複数の条件があります)、クエリにかなりの時間がかかることです。

SELECT prism_actions.id FROM prism_actions WHERE prism_actions.action_type = 'block-break' AND prism_actions.block_id = 2 LIMIT 1000;
1000 rows in set (0.79 sec)

完全なクエリでは .79 秒が許容されますが、それは条件の一部を使用しているだけです。

実際のクエリは次のようになります。

SELECT prism_actions.id FROM prism_actions WHERE prism_actions.action_type = 'block-break' AND prism_actions.player = 'viveleroi' AND prism_actions.block_id = 2 LIMIT 1000;
1000 rows in set (2.22 sec)

単一の条件では で実行し0.01、2 では実行し、0.793 では2.2長すぎる秒で実行しました。

インデックスをより適切に設計する方法を調査しますが、現在のデータベース スキーマとインデックスにほとんど満足しています。

しかし、そのように組み合わせて条件を速くするにはどうすればよいでしょうか?

アップデート

テーブルを外部キー形式に変換するのに時間を費やしました。player、action_type、world 列のデータは別のテーブルに移動され、それらの ID は元のテーブルに格納されました。データの移行に数時間かかりました。

ただし、以前に使用したのと同じクエリを再実行しています。速度が向上したものもあれば、ほとんど変化が見られないものもあります。

上記の 0.79 秒のクエリを変換したバージョンは、ほぼ同じ速度で実行されます。

SELECT prism_actions.id FROM prism_actions WHERE prism_actions.actiontype_id = 1 AND prism_actions.block_id = 2 LIMIT 1000;
1000 rows in set (0.73 sec)

block_id 列には、元のテーブル スキーマからのインデックスがまだあります。

条件として player_id を使用したクエリの実行が非常に遅かったため、列にインデックスを追加したところ、クエリが非常に高速になりました。

ただし、実際のユーザーからいくつかのクエリの例を取得し、このテーブル構造に合わせて更新した後、速度に変化は見られません.

SELECT prism_actions.id FROM prism_actions WHERE (prism_actions.actiontype_id = 2 OR prism_actions.actiontype_id = 1) AND (prism_actions.player_id = 1127) AND prism_actions.action_time >= '2013-02-22 07:47:54' LIMIT 1000;

以前は5.83 sec、現在は5.29 sec

編集 - タイムスタンプのようです。上記のクエリからタイムスタンプ条件を除外すると、0.01 秒で結果が返されます。タイムスタンプのインデックスを追加しても何も起こりません - アイデアですか?

これまでのところ、私が実際に目にしているのは、特定の領域でのわずかな速度の向上と、重複した文字列を保存するためのファイル スペースのわずかな節約だけです。データを変換する時間の。

他の方法でインデックスを作成するなどの提案はありますか?

4

3 に答える 3

0

すべてのテキスト列(アクションタイプ、プレーヤー、ワールド)を新しいテーブルに移動します。これらはすべてテキスト列です。

これにより、dbサイズが削減され、参照番号がこのテーブルに保持されます。

これにより、パフォーマンスが劇的に向上します。

于 2013-03-01T17:16:24.073 に答える
0

結果セットの後に再度参加する必要がないように、テーブルをそのままにしておきます。where のすべてのキー列を含む単一のインデックスが必要であり、それぞれが個別に必要なわけではありません。最初にヒットする最小の結果セット (2200 万レコードなど) に基づいてこれを最適化しようとします。かなりの数が Block_ID = 2 に基づいており、プレーヤーに基づいていることははるかに少ないと思います。

だから、私はインデックスを持っているでしょう

create index multipart on prism_actions ( Player, Block_ID, Action_Type );

現在持っている個々のフィールドではなく、単一のインデックスとして。これにより、エンジンは特定のプレーヤーに直接ジャンプできます。現在は 2200 万から、たとえば 2000 エントリまで、ブロック ID = 2 から 200 まで、action_type = ブロック ブレークまで.... 20 レコード ... 明らかにちょうどレコード数の任意のサンプルですが、必要なのは複合インデックスだけです。

于 2013-03-01T20:21:48.387 に答える
0

MySQL v5.5: 次のように作成できますPARTITION BY RANGE COLUMNS

CREATE TABLE `prism_actions` (
  `id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  `action_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `action_type` varchar(25) NOT NULL,
  `player` varchar(16) NOT NULL,
  `world` varchar(255) NOT NULL,
  `x` int(11) NOT NULL,
  `y` int(11) NOT NULL,
  `z` int(11) NOT NULL,
  `block_id` mediumint(5) UNSIGNED NOT NULL,
  `block_subid` mediumint(5) UNSIGNED NOT NULL,
  `data` varchar(255) NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `x` (`x`),
  KEY `action_type` (`action_type`),
  KEY `player` (`player`),
  KEY `block_id` (`block_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1
PARTITION BY RANGE COLUMNS(action_type, player, block_id)(
PARTITION p0 VALUES LESS THAN ('dddddddd','dddddddd',1000000),
PARTITION p1 VALUES LESS THAN ('gggggggg','gggggggg',2000000),
PARTITION p2 VALUES LESS THAN ('jjjjjjjj','jjjjjjjj',3000000),
PARTITION p3 VALUES LESS THAN ('mmmmmmmm','mmmmmmmm',4000000),
PARTITION p4 VALUES LESS THAN ('pppppppp','pppppppp',5000000),
PARTITION p5 VALUES LESS THAN ('ssssssss','ssssssss',6000000),
PARTITION p6 VALUES LESS THAN ('uuuuuuuu','uuuuuuuu',7000000),
PARTITION p7 VALUES LESS THAN (MAXVALUE,MAXVALUE,MAXVALUE)
);

最悪の場合、任意のセットが(action_type, player, block_id)1 つのパーティションにのみ属することができます。したがって、元のクエリと比較して優れています。

列値の頻度分布を分析し、それに応じてパーティションを作成できる場合、ボーナス速度。上記のパーティションは大まかな間隔です。

于 2013-03-01T17:56:20.563 に答える