6

MySQL4.xDBには次の3つのテーブルがあります。

  • ホスト:(300.000レコード)
    • id(UNSIGNED INT)主キー
    • 名前(VARCHAR 100)
  • パス:(6.000.000レコード)
    • id(UNSIGNED INT)主キー
    • 名前(VARCHAR 100)
  • URL:(7.000.000レコード)
    • host(UNSIGNED INT)PRIMARY KEY<---hosts.idへのリンク
    • path(UNSIGNED INT)PRIMARY KEY<---paths.idへのリンク

ご覧のとおり、スキーマは非常に単純ですが、問題はこれらのテーブルのデータ量です。

これが私が実行しているクエリです:

SELECT CONCAT(H.name, P.name)
FROM hosts AS H
INNER JOIN urls as U ON H.id = U.host
INNER JOIN paths AS P ON U.path = P.id;

このクエリは完全に正常に機能しますが、実行には50分かかります。誰かが私がそのクエリをどのようにスピードアップできるかについて何か考えがありますか?

前もって感謝します。ニコラス

4

14 に答える 14

6

おそらく、WHERE 句を含める必要がありますか? それとも、本当にすべてのデータが必要ですか?

于 2009-02-04T13:56:48.143 に答える
4

これは、代理キーの過度の使用が速度を落としているケースのように私には思えます。テーブルが次の場合:

  • ホスト:

    • 名前 (VARCHAR 100) PRIMARY KEY
  • パス:

    • 名前 (VARCHAR 100) PRIMARY KEY
  • URL :

    • host (VARCHAR 100) PRIMARY KEY <--- hosts.name へのリンク
    • path (VARCHAR 100) PRIMARY KEY <--- paths.name へのリンク

次に、クエリは結合をまったく必要としません。

SELECT CONCAT(U.host, U.path) FROM urls U;

確かに、テーブル URL はより多くのディスク スペースを占有しますが、それは問題なのでしょうか?

編集:考え直して、とにかくそのPATHSテーブルのポイントは何ですか? 異なるホストが同じパスを共有する頻度は?

なぜだめですか:

  • ホスト:

    • 名前 (VARCHAR 100) PRIMARY KEY
  • URL :

    • host (VARCHAR 100) PRIMARY KEY <--- hosts.name へのリンク
    • path (VARCHAR 100) PRIMARY KEY <--- どこにもリンクなし

EDIT2:または、ホストの代理キーが本当に必要な場合:

  • ホスト:

    • ID 整数 PRIMARY KEY
    • 名前 (VARCHAR 100)
  • URL :

    • host integer PRIMARY KEY <--- hosts.name へのリンク
    • path (VARCHAR 100) PRIMARY KEY <--- どこにもリンクなし

    SELECT CONCAT(H.name, U.path) FROM urls U JOIN hosts H ON H.id = U.host;

于 2009-02-04T14:09:41.347 に答える
2

全体として、最善のアドバイスは、実際に時間を費やしているものを確認するためにトレースしてプロファイルを作成することです。しかし、ここに見るべき特定の事柄についての私の考えがあります。

(1)このクエリの実行でインデックスが使用されないようにする必要があります。フィルタリング条件がないため、すべてのテーブルをフルスキャンしてから、ソートマージまたはハッシュ操作でそれらを結合する方が効率的です。

(2)文字列の連結には確かに時間がかかりますが、なぜ人々がそれを削除することを勧めているのかわかりません。おそらく、別のコードで連結を行う必要がありますが、それでもほぼ同じ時間がかかります(MySQLの文字列連結が何らかの理由で特に遅い場合を除く)。

(3)サーバーからクライアントへのデータ転送には、おそらくかなりの時間がかかります。サーバーがデータをフェッチするのに必要な時間よりもかなり長い可能性があります。この種のものを追跡するためのツールがある場合は、それらを使用してください。クライアントでフェッチ配列のサイズを増やすことができる場合は、さまざまなサイズを試してください(たとえば、JDBCではStatement.setFetchSize()を使用します)。これは、クライアントとサーバーが同じホスト上にある場合でも重要になる可能性があります。

于 2009-02-04T14:41:57.333 に答える
2

1 つには、クエリで CONCAT を実行しません。外でやってください。

しかし、何百万もの行を取得しているため、実際にはクエリの実行が遅くなります。

于 2009-02-04T13:58:36.813 に答える
1

サーバー構成を確認する必要があります。MySQLのデフォルトのメモリパラメータは、そのサイズのテーブルのパフォーマンスを低下させます。デフォルトを使用している場合は、少なくとも4倍、おそらくそれ以上に引き上げる必要がありkey_buffer_sizeますjoin_buffer_size。ドキュメントを見てください。微調整できる他のメモリパラメータがあります。

MySQLには、ほとんどのデータを返すクエリでテーブルが特定のサイズを超えると、パフォーマンスが低下するという面白いパフォーマンスの癖があります。残念ながら、そのしきい値に達したときに通知する方法はありません。しかし、私にはあなたが持っているように見えます。

于 2009-02-06T06:28:02.413 に答える
1

私はMySQLの専門家ではありませんが、MySQLの主キーがクラスター化されているようです。主キーがそうであることを確認する必要があります。クラスター化インデックスは間違いなくスピードアップに役立ちます。

ただし、1つだけですが、どのテーブルにも2つの「主」キーを設定できるとは思いません。そのため、URLテーブルは私にはかなり疑わしいように見えます。何よりも、urlsテーブルのこれらの2つの列が柄にインデックス付けされていることを絶対に確認する必要があります-それぞれに単一の数値インデックスが適切である必要があります-それらに結合しているため、DBMSはその方法を知る必要がありますそれらをすばやく見つけます。それがあなたの場合に起こっていることかもしれません。その数の行をフルテーブルスキャンしている場合は、そうです。サーバーが要求されたすべてのものを見つけようとしている間、かなりの時間そこに座っている可能性があります。

また、そのCONCAT関数をselectステートメントから削除し、それが結果にどのように影響するかを確認することをお勧めします。それがどういうわけか貢献要因でなければ、私は驚かれることでしょう。両方の列を取得し、後で連結を処理して、それがどのように行われるかを確認してください。

最後に、ボトルネックがどこにあるかを把握しましたか?テーブルが適切にインデックス付けされていれば、3つの数百万行のテーブルに参加するだけでもそれほど時間はかからないはずです(テーブルとクエリを目で確認するだけで、おそらく1秒ほどかかると思います)。ただし、これらの行を低速またはすでにペグされているNICを介して、メモリが不足しているアプリサーバーなどにプッシュしている場合、速度の低下はクエリとはまったく関係がなく、クエリの後に何が起こるかとは関係ありません。700万行は、それらの行の検出にかかる時間に関係なく、組み立てて移動するためのかなりの量のデータです。700万行すべてではなく、1行だけを選択してみて、対照的にどのように見えるかを確認してください。それが速い場合、問題はクエリではなく、結果セットです。

于 2009-02-04T14:33:21.033 に答える
1

取得したいデータで新しいテーブルを作成しようと思います。これを行うと、実際のデータの一部が失われますが、迅速に勝つことができます。このアイデアは OLAP などに似ているのでしょうか?

もちろん、このテーブルの更新 (毎日または何でも) を行う必要があります。

于 2009-02-04T14:04:15.600 に答える
1

結果セットはすべてのデータを返すため、実行できる最適化はほとんどありません。テーブル全体をスキャンしてから、インデックスを持つ他のテーブルに参加しています。

主キーはクラスター化されていますか? これにより、データがインデックス順にディスクに保存されるため、ディスクの別の部分にバウンスすることを回避できます。

また、データを複数のディスクに分散させることもできます。PRIMARY に URL があり、SECONDARY に PATHS/HOSTS がある場合、ドライブからのスループットが向上します。

于 2009-02-04T15:15:55.753 に答える
1

join-attributes ですでにいくつかのインデックスを宣言していますか?

PS: MySQL 4.x のインデックスについては、こちら[壊れたリンク] を参照してください。

于 2009-02-04T13:57:50.340 に答える
1

クエリを実行する前に、テーブルを最適化してみてください。

optimize table hosts, paths, urls;

特にテーブルから行が削除されている場合は、時間を節約できます。(OPTIMIZEの詳細については、こちらを参照してください)

于 2009-02-04T14:02:02.640 に答える
0

私は MySQL の大ファンではないので、PostgreSQL を試したことがあるかどうかお尋ねします。その DB では、work_mem 設定がかなり高いことを確認する必要がありますが、たとえば、SET work_mem = 64MB を使用して DB 接続ごとに設定できます。

もう 1 つの提案は、重複したパス エントリの使用を検討することです。パスを共有する URL多数あります。

役立つかもしれないし、役に立たないかもしれないもう 1 つのことは、varchar の代わりに固定長のテキスト フィールドを使用することです。以前は速度に違いがありましたが、現在の DB エンジンについてはわかりません。

PostgreSQL を使用する場合は JOIN USING を使用できますが、MySQL でさえ私はそれが好きです: すべてのテーブルで同じ id フィールドに名前を付けます。ホストの id と URL のホストの代わりに、両方の場所で host_id という名前を付けます。

では、さらに解説を。:) ここにあるこのデータ レイアウトは、行の小さなセット、おそらく同じドメインからのすべての URL を選択する場合に非常に役立ちます。また、クエリで urls テーブルに保存されている他のデータのシーケンシャル スキャンを頻繁に実行する必要がある場合にも役立ちます。これは、スキャンが大きなテキスト フィールドをスキップできるためです (DB がポインターを介してテキストを保存するため問題にならない場合を除きます)。とにかくリンクされたテーブル)。

ただし、ほとんどの場合、すべてのドメインとパスのデータを選択する場合は、1 つのテーブルに格納する方が理にかなっています。

于 2009-02-06T07:00:11.053 に答える
0

連結は間違いなくあなたを遅くしています。これについてmysqlの説明の結果を見ることができますか? ドキュメント リンク

ただし、最も重要なことは、必要なデータのみをプルすることです。プルするレコード数を減らすことができれば、何よりもスピードアップします。しかし、mysql の説明は、インデックスが役立つかどうかを確認するのに役立ちます。

于 2009-02-04T14:02:40.470 に答える