2010-12-01 19 views
2

私は自分のシステムから送信されたすべてのメッセージを格納するInnoDBを使用しているテーブルを持っています。現在、この表には4,000万行があり、月に3百万回増加しています。大きなテーブルを持つMysql:このクエリを最適化する方法?

私の質問は基本的に、ユーザーから送信されたメッセージとデータ範囲内のメッセージを選択することです。ここでは単純なテーブルを作成しています:

CREATE TABLE `log` (
    `id` int(10) NOT NULL DEFAULT '0', 
    `type` varchar(10) NOT NULL DEFAULT '', 
    `timeLogged` int(11) NOT NULL DEFAULT '0', 
    `orig` varchar(128) NOT NULL DEFAULT '', 
    `rcpt` varchar(128) NOT NULL DEFAULT '', 
    `user` int(10) DEFAULT NULL, 
    PRIMARY KEY (`id`), 
    KEY `timeLogged` (`timeLogged`), 
    KEY `user` (`user`), 
    KEY `user_timeLogged` (`user`,`timeLogged`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

注:他のクエリのために個別のインデックスもあります。

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

SELECT COUNT(*) FROM log WHERE timeLogged BETWEEN 1282878000 AND 1382878000 AND user = 20

問題は、このクエリは、ページがロードされるのを待つためにあまりにも多くの時間をあるユーザとサーバの負荷に依存し、10分に2分かかることです。私はmysqlキャッシュを有効にしてアプリケーションでキャッシュしていますが、問題は新しい範囲をユーザーが検索したときにキャッシュにヒットしないということです。

私の質問は以下のとおりです。

  • はuser_timeLoggedインデックスは任意の違いを作る変えますか?
  • これはMySQLと大きなデータベースに問題がありますか?つまり、Oracleや他のDBもこの問題を抱えていますか?

AFAIKでは、私のインデックスは正しく作成されており、このクエリはあまり時間がかかりません。

お手数をおかけしていただきありがとうございます。

+2

ポストtimeLoggedログからEXPLAIN '次のSELECT COUNT(*)との間から出力(全てのクエリは冷たいバッファを実行します) 1282878000とAND 1382878000 AND user = 20; –

+0

これをコメントとして投稿します。クエリ最適化には対応していませんが、すべてのメッセージを1つのテーブルに保存するのではなく、アーカイブ戦略を検討しましたか?月間750kの40mレコードは、4年分のデータを意味します。任意の年齢のメッセージが同じ頻度で照会されることが本当にある場合を除き、古いメッセージを別のテーブルに移動し、古いメッセージの要求をそのテーブルに送るロジックを実装することを検討するとよいでしょう。 –

+0

クエリで最適化することはあまりありません。 * key_buffer *のサイズはどれくらいですか? – AndreKR

答えて

0

COUNT(*)がWHERE句を持っているためにテーブルキャッシュからロードされていません。前述の@jasonとしてEXPLAINを使用して、COUNT(id)に変更してください。

私は間違っている可能性がありますが、あなたのインデックスはWHERE句と同じ順序でなければならないと思います。あなたのWHERE句がusertimeLoggedを使用しているので、あなたのインデックスはKEY(,ユーザ)timeLogged user_timeLoggedする必要があります `

ここでも、この屈折率変化が違いを作るかどうかを教えてくれますEXPLAIN。

+2

実際には、同じwhere句で書かれた制約はどのような順序でも違いはありません。それを処理するクエリオプティマイザジョブです。 timeloggedは範囲スキャンなので、最後の部分として使用されます。インデックスは大丈夫だと思うかもしれませんが、多分テーブルから多すぎるものを選んでいると(テーブル全体のスキャンになります)、dbの設定が間違っています。 – johno

1

あなたはInnoDBテーブルを使用していますが、一般的なクエリの形式は同じように見えるよう、あなたのInnoDBクラスタ化インデックス(主キー)をフル活用していない:

select <fields> from <table> where user_id = x and <datefield> between y and z 

ない

select <fields> from <table> where id = x 

次の記事は、クエリのテーブルデザインを最適化するのに役立ちます。

http://www.xaprb.com/blog/2006/07/04/how-to-exploit-mysql-index-optimizations/

あなたが正しく記事を理解していれば、あなたは、次のようなものでyouselfを見つける必要があります。

drop table if exists user_log; 
create table user_log 
(
user_id int unsigned not null, 
created_date datetime not null, 
log_type_id tinyint unsigned not null default 0, -- 1 byte vs varchar(10) 
... 
... 
primary key (user_id, created_date, log_type_id) 
) 
engine=innodb; 

ここで上記の設計からいくつかのクエリパフォーマンスの統計です:

カウント

は、
select count(*) as counter from user_log 

counter 
======= 
37770394 

select count(*) as counter from user_log where 
created_date between '2010-09-01 00:00:00' and '2010-11-30 00:00:00' 

counter 
======= 
35547897 

ユーザーと日付ベースのクエリーは

select count(*) as counter from user_log where user_id = 4755 

counter 
======= 
7624 

runtime = 0.215 secs 


select count(*) as counter from user_log where 
user_id = 4755 and created_date between '2010-09-01 00:00:00' and '2010-11-30 00:00:00' 

counter 
======= 
7404 

runtime = 0.015 secs 

select 
user_id, 
created_date, 
count(*) as counter 
from 
user_log 
where 
user_id = 4755 and created_date between '2010-09-01 00:00:00' and '2010-11-30 00:00:00' 
group by 
user_id, created_date 
order by 
counter desc 
limit 10; 

runtime = 0.031 secs 

希望はこれが:)役立ちます

関連する問題