26

次のデータベース テーブルを検討してください。

  • 13,000,000 行 (メッセージごとに 1 行) のテーブル「メッセージ」。
  • 3,000,000 行 (ユーザーごとに 1 行) のテーブル "users"。

次のクエリは、一連のメッセージと対応するユーザーを取得するために使用されます。

SELECT messages.id, messages.message, users.id, users.username
FROM messages
INNER JOIN users ON messages.user_id=users.id 
WHERE messages.id in (?, ?, ?, ? ... a total of 100 "?":s);

各クエリで 100 件のメッセージがフェッチされます。

「メッセージ」は、id (主キー、自動生成されないBIGINT ) および user_id でインデックス化されます。

「users」は id でインデックス化されます (主キー、INT 自動生成)。

データベースは MyISAM を使用した MySQL です。

現在、「メッセージ」は「id」でインデックス化されているため、クエリの実行には3000ミリ秒以上かかるため、正しい行を取得するのは非常に迅速です。

私の質問は次のとおりです:記述シナリオとセットアップを考えると、3000ミリ秒のクエリ時間は「正常」ですか、それとも何か不足していますか? さらに詳細が必要な場合はお知らせください。

更新 #1:テーブルの定義は次のとおりです。

CREATE TABLE messages (
  id bigint(20) NOT NULL DEFAULT '0',
  user_id int(11) NOT NULL DEFAULT '0',
  message varchar(160) NOT NULL DEFAULT '',
  PRIMARY KEY (id),
  KEY user_id (user_id),
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

CREATE TABLE users (
  id int(11) NOT NULL DEFAULT '0',
  username varchar(32) NOT NULL DEFAULT '',
  PRIMARY KEY (id),
  UNIQUE KEY username (username),
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

定義で私が観察した唯一の「非標準」のことは、「messages.id」がINTではなくBIGINTであることです。それがヒントになりませんか?

4

7 に答える 7

11

私は数十億行のMyISAMテーブルで作業しましたが、行数の制限の後で見つけたものの1つは、オプティマイザーがクエリへのアプローチ方法を決定するのに時間がかかりすぎ、テーブルスキャンを誤って実行したことです。それを説明している正確なページを見つけることができませんが、オブジェクトを要求する方法を知っているクエリの各セグメントで常にFORCE_INDEXを使用し始めました

http://dev.mysql.com/doc/refman/5.1/en/index-hints.html

実際のところ、これほど大きなテーブルを使用している場合は、インデックスを操作するためにすべてのクエリを設計する必要があるため、インデックスを強制することに問題はありません。必要に応じてテーブルをスキャンしますが、FORCE_INDEXは、絶対に必要な場合を除いて、スキャンしないように指示します。

また、テーブルが大きい場合は、インデックスも大きいと思います。適切な構成設定があり、key_bufferが適切なサイズであり、十分なI/Oがあることを絶対に確認する必要があります。32ビットmysqlを実行している場合(これは実行すべきではありません)、key_bufferを1GB(1GBの余裕があると仮定)に設定し、「mysqlreport」でその使用法を確認します。

64ビットmysqlを実行している場合は、OSがファイルや実行中の他のアプリケーションをキャッシュするためのスペースを残したまま、できるだけ大きくすることを選択します。可能であれば、数GBになる可能性があります。

クエリでインデックスを使用している場合でも、インデックスをメモリに適切にバッファリングできない場合は、ディスクにアクセスしていることになり、インデックスのサイズとディスク/使用可能なI/Oの速度に比例したパフォーマンスの低下が発生します。

intとbigintに関する限り、私が見た唯一の顕著なパフォーマンスの違いは、SUMなどの大きなintで計算を実行することです。SUMは、intよりもbig intの方がかなり遅いので、数値を異なる大きさで格納するか、頻繁に計算する必要がある場合は2つのintに分割することを検討します。

于 2009-07-28T09:24:36.797 に答える
6
  1. 説明が必要です。
  2. MyISAMは同時実行性が低くなっています。同時挿入が頭痛の種になる可能性があることを考慮してください。このような大規模なデータベースでは、InnoDBが進むべき方向かもしれません。
  3. メッセージが挿入および削除されている場合、テーブルがときどき最適化されていないと、状況が歪む可能性があります。また、MyISAMの主キーはクラスター化されていません。繰り返しになりますが、このような大規模なデータベースでは、InnoDBが進むべき方向かもしれません。
于 2009-07-07T17:56:08.007 に答える
3
SELECT  messages.id, messages.message, users.id, users.username
FROM    messages
INNER JOIN
        users
ON      users.id = messages.user_id
WHERE   messages.id in (?, ?, ?, ? ... a total of 100 "?":s);

メッセージにはデータ型TEXTがあり、長いようです。

長いTEXT列は行外に格納されるため、それらを取得するために追加のページ読み取りを行う必要があり、時間がかかる場合があります。

以下の2点をご確認いただけますでしょうか。

  1. このクエリのパフォーマンス:

    SELECT  messages.id, users.id, users.username
    FROM    messages
    INNER JOIN
            users
    ON      users.id = messages.user_id
    WHERE   messages.id in (?, ?, ?, ? ... a total of 100 "?":s);
    
    • このクエリと元のクエリによって生成された実行計画。
于 2009-07-07T15:04:57.450 に答える
1

クエリとテーブルの設計自体が原因ではない可能性があります。クエリはいくつかの助けを使用できますが(オプティマイザーは関係なく同じプランを返すと思いますが、レイトサイドフィルターを排除するために「リスト内」を結合述語に追加するなど)

私の推測では、これは他の問題、インデックス\テーブルの断片化、または古い統計の兆候です。これらのテーブルは頻繁に削除されますか? テーブルとインデックスを最適化すると役立つ場合があります。そうしないと、10% 以下しか使用されていないページの被害者になる可能性があり、大量のディスク I/O が発生します。

注:主キーに整数シードを使用すると、行の削除と更新を頻繁に行わない限り、多くの断片化は見られません。

于 2009-07-07T17:03:53.340 に答える
0

現在、クエリの実行には 3000 ミリ秒以上かかります

毎回ですか、それとも最初のクエリだけですか? 最初のクエリでインデックスの読み込みなどのコストが発生している可能性はありますか?

比較のために、特定のメッセージ ID に対して同じクエリを実行するには、どのくらいの時間がかかりますか?

また、これを実行しているボックスの仕様にもよりますが、他の人が提案した実行計画を調べるだけでなく、mysqld のメモリ使用量を調べて、単純にスワップしていないことを確認する価値があるかもしれません。

于 2009-07-07T16:35:48.030 に答える
0

これは通常、パーサーによって次のように書き直されるためです。

SELECT messages.id, messages.message, users.id, users.username
FROM messages
INNER JOIN users ON messages.user_id=users.id 
WHERE messages.id = ?
OR messages.id = ?
OR messages.id = ? etc.

単一のケースの実行計画とパフォーマンスを確認したいと思います。

SELECT messages.id, messages.message, users.id, users.username
FROM messages
INNER JOIN users ON messages.user_id=users.id 
WHERE messages.id = ?

その場合、 を実行するUNIONか、ID を含むテーブルを作成してJOIN.

于 2009-07-07T17:09:42.547 に答える
0

ここで見ているハードウェアは何ですか?妥当な量の RAM と key_buffer が非常に大きく設定されたサーバーがあると仮定します (たとえば、2 つの適度なサイズのテーブルの合計インデックス サイズよりも大きい)。サーバーは、それ以外の場合はアイドル状態のパフォーマンス テスト サーバーであると想定しています。

IO の量を測定できますか?

まったく同じクエリを繰り返した場合、高速ですか?

データベース全体を RAM ディスクにロードすると (1,500 万行しかない小さなテーブルは、RAM ディスクに簡単に収まります)、高速になりますか?

また(他の人が指摘したように)、EXPLAIN計画を投稿してください。

しかし、このような小さなデータベースは、非常に小さなサーバーを除くすべてのサーバーの RAM に収まるため、常に高速である必要があります。

于 2009-07-07T21:02:07.553 に答える