1

customers次の構造のMySQL5.1InnoDBテーブル( )があります。

int         record_id (PRIMARY KEY)
int         user_id (ALLOW NULL)
varchar[11] postcode (ALLOW NULL)
varchar[30] region (ALLOW NULL)
..
..
..

テーブルには約700万行あります。現在、テーブルは次のようにクエリされています。

SELECT * FROM customers WHERE user_id IN (32343, 45676, 12345, 98765, 66010, ...

実際のクエリでは、現在560user_id秒以上がIN句に含まれています。テーブルに数百万のレコードがあるため、このクエリは低速です。

テーブルにはセカンダリインデックスがあり、最初のインデックスはuser_idそれ自体にあり、役立つと思いました。

私はそれSELECT(*)が悪いことであることを知っています、そしてこれは必要なフィールドの完全なリストに拡張されます。ただし、上記にリストされていないフィールドは、より多くintのsとdoublesです。返送されたものはさらに50個ありますが、レポートに必要です

のデータにアクセスするためのはるかに優れた方法があるuser_idと思いますが、その方法を考えることはできません。私の最初の反応は、処理がクエリの速度を低下させることを理解しているので、フィールドALLOW NULL上のを削除することです。user_idNULL

この方法を使用するよりも効率的な方向に私を向けていただければ幸いですIN ( )

ランEXPLAINを編集します。

select_type = SIMPLE 
table = customers 
type = range 
possible_keys = userid_idx 
key = userid_idx 
key_len = 5 
ref = (NULL) 
rows = 637640 
Extra = Using where 

それは役に立ちますか?

4

5 に答える 5

3

まず、にインデックスがあるかどうUSER_ID かを確認し、それが使用されていることを確認します。

を実行してそれを行うことができますEXPLAIN

次に、一時テーブルを作成し、それをJOIN:で使用します。

CREATE TABLE temptable (user_id INT NOT NULL)

SELECT  *
FROM    temptable t
JOIN    customers c
ON      c.user_id = t.user_id

第三に、クエリはどのように行を返しますか?

ほぼすべての行が返される場合は、そもそも接続チャネルを介してこれらの数百万のすべてをポンピングする必要があるため、速度が遅くなります。

NULL条件はインデックス付けされた非値INのみを満たすため、クエリの速度が低下することはありません。NULL

アップデート:

インデックスが使用されます。50万行以上を返すことを除いて、計画は問題ありません。

638,000これらすべての行をレポートに含める必要が本当にありますか?

印刷されていないことを願っています:熱帯雨林、地球温暖化などに悪いです。

真剣に言えば、クエリで集計またはページ付けが必要なようです。

于 2009-05-25T16:37:42.960 に答える
2

「Select*」は、一部の人が考えるほど悪くはありません。行ベースのデータベースは、行のいずれかをフェッチすると行全体をフェッチするため、カバーインデックスを使用していない状況では、「SELECT*」は基本的に「SELECTa、b、c」よりも遅くありません(注:大きなBLOBがある場合は例外になることがありますが、それはエッジケースです)。

まず最初に-データベースはRAMに収まりますか?そうでない場合は、RAMを増やしてください。いいえ、真剣に。ここで、データベースが大きすぎてRAMに適度に収まらない場合(たとえば、> 32Gb)、ランダムI / Oの数を減らすようにしてください。これは、おそらく問題を引き起こしているためです。

ここからは、RAID1(またはRAID10など)のRAIDコントローラーと少なくとも2つのスピンドルを備えた適切なサーバーグレードのハードウェアを実行していると想定します。そうでない場合は、離れてそれを入手してください。

クラスター化されたインデックスの使用を検討することは間違いありません。MySQL InnoDBでは、主キーのみをクラスター化できます。つまり、現在他の何かが主キーである場合は、それを変更する必要があります。複合主キーは問題ありません。1つの基準(たとえばuser_id)で多くのクエリを実行する場合は、それを主キーの最初の部分にすることは明確な利点です(それを作成するには他に何かを追加する必要があります)個性的)。

または、クエリでカバーインデックスを使用できる場合もあります。その場合、user_idを主キーにする必要はありません(実際にはそうではありません)。これは、必要なすべての列がuser_idで始まるインデックスにある場合にのみ発生します。

クエリの効率に関する限り、WHERE user_id IN(IDの大きなリスト)は、SQLからそれを行う最も効率的な方法です。

しかし、私の最大のヒントは次のとおりです。

  • 目標を念頭に置き、それが何であるかを理解し、それに到達したら停止します。
  • 誰かの言葉を信じないでください-試してみてください
  • パフォーマンステストシステムが本番環境と同じハードウェア仕様であることを確認してください
  • パフォーマンステストシステムのデータサイズと種類が本番環境と同じであることを確認してください(同じスキーマでは不十分です!)。
  • 本番データを使用できない場合は、合成データを使用します(本番データのコピーは、ロジスティック的に困難な場合があります(データベースが> 32Gbであることを忘れないでください)。セキュリティポリシーに違反する可能性もあります)。
  • クエリが最適である場合(おそらくすでにそうであるように)、スキーマを調整してから、データベース自体を調整してみてください。
于 2009-05-25T20:36:40.323 に答える
1

それらは毎回同じ〜560 IDですか?それとも、クエリの実行ごとに異なる最大500 IDですか?

560個のユーザーIDを別のテーブル(または一時テーブル)に挿入し、そのテーブルにインデックスを貼り付けて、元のテーブルに内部結合するだけです。

于 2009-05-25T16:41:27.543 に答える
1

これはあなたの最も重要な質問ですか?これはトランザクションテーブルですか?

その場合は、user_idにクラスター化されたインデックスを作成してみてください。一致するレコードを見つけた後でも(user_Idインデックスのインデックスシーク)、列を取得するためにランダムなディスク読み取り(キールックアップ)を行う必要があるため、クエリは遅くなる可能性があります。

クラスタ化インデックスを変更できない場合は、ETLプロセスを検討することをお勧めします(最も単純なのは、最適なインデックスを持つ別のテーブルに挿入するトリガーです)。これにより、より速い結果が得られるはずです。

また、このような大規模なクエリは解析に時間がかかる場合があるため、可能であれば、クエリされたIDを一時テーブルに配置することで解決できることにも注意してください。

于 2009-05-25T16:41:29.477 に答える
0

クエリする必要のあるIDを一時テーブルに挿入し、両方のテーブルを内部結合することができます。それが役立つかどうかはわかりません。

于 2009-05-25T16:39:08.920 に答える