2012-04-11 6 views
1

かなり大きなデータセットの単純なクエリであると思うものを実行しようとしていますが、実行に非常に時間がかかります。 -4時間以上。MySQLは永遠にデータを送信しています。単純なクエリ、たくさんのデータ

表は次のようになります。

CREATE TABLE `transaction` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, 
`uuid` varchar(36) NOT NULL, 
`userId` varchar(64) NOT NULL, 
`protocol` int(11) NOT NULL, 
... A few other fields: ints and small varchars 
`created` datetime NOT NULL, 
PRIMARY KEY (`id`), 
KEY `uuid` (`uuid`), 
KEY `userId` (`userId`), 
KEY `protocol` (`protocol`), 
KEY `created` (`created`) 
) ENGINE=InnoDB AUTO_INCREMENT=61 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPRESSED KEY_BLOCK_SIZE=4 COMMENT='Transaction audit table' 

そして、クエリはここにある:

select protocol, count(distinct userId) as count from transaction 
where created > '2012-01-15 23:59:59' and created <= '2012-02-14 23:59:59' 
group by protocol; 

表は約2.22億行を持っており、クエリのWHERE句を約20にまでフィルタ百万行distinctオプションはそれを約700,000の別個の行にし、グループ化した後(そして最後にクエリが終了すると)、実際には4〜5行が返されます。

私はそれが大量のデータであることを認識していますが、このクエリでは4時間から5時間が非常に長いようです。

ありがとうございました。

編集:参考のために、これはAWSのdb.m2.4xlarge RDSデータベースインスタンスで実行されています。

答えて

3

これは非常に重いクエリです。なぜそれが長くかかるのか理解するためには、詳細を理解する必要があります。

インデックスフィールドに範囲条件があります。つまり、MySQLはインデックス内で最小の作成値を見つけ、インデックスから対応するプライマリキーを取得し、そのレコードをディスクから取り出し、必要なフィールドを取得します(protocol、userId)が現在の索引レコードにない場合は、それらを700000行にグループ化して「一時表」に入れます。インデックスは実際に使用することができ、ここでは範囲条件の高速化にのみ使用されます。

高速化する唯一の方法は、MySQLが行のディスク参照を行う必要がないように、すべての必要なデータを含むインデックスを持つことです。それはcovering indexと呼ばれます。しかし、インデックスはメモリ内に存在し、〜sizeOf(created+protocol+userId+PK)*rowCountバイトを含むことになります。これは、テーブルを更新するクエリや他のインデックスにとっては負担になります。個別の集計テーブルを作成し、クエリを使用してテーブルを定期的に更新する方が簡単です。

+0

+1。先行列 'created'を持つ別の索引を追加すると、' created'列のOP索引が冗長になります。 'created'カラムのインデックスを削除することができます。 'protocols'列と' created'列を入れ替えて別のカバー索引を作成すると、パフォーマンスが向上する可能性があります。その場合、 'protocol'列の別のインデックスは冗長になります。 – spencer7593

1

別名とグループ別の両方で、サーバーに一時データをソートして保存する必要があります。その多くのデータで、しばらく時間がかかることがあります。

userIdと作成されたプロトコルとプロトコルのさまざまな組み合わせを索引付けすると役立ちますが、どれだけ多くのインデックスが役立つのかはわかりません。

+0

'カバリングインデックス'は、(この特定のクエリのパフォーマンスが)最も役立つでしょう。トレードオフは、インデックスを維持するために必要なリソースです。 – spencer7593

11

クエリをプロファイルして、正確に何が起こっているのかを確認してください。

SET PROFILING = 1; 
SET profiling_history_size = 0; 
SET profiling_history_size = 15; 
/* Your query should be here */ 
SHOW PROFILES; 
SELECT state, ROUND(SUM(duration),5) AS `duration (summed) in sec` FROM information_schema.profiling WHERE query_id = 3 GROUP BY state ORDER BY `duration (summed) in sec` DESC; 
SET PROFILING = 0; 
EXPLAIN /* Your query again should appear here */; 

これは、正確なクエリに時間がかかり、結果に基づいて最適化操作を実行するのに役立つと思います。

関連する問題