4

私は単純な招待テーブルを持っています:

CREATE TABLE `invitation` (
  `invitation_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `inviter_id` int(10) unsigned NOT NULL,
  `invitee_id` int(10) unsigned NOT NULL,
  PRIMARY KEY (`invitation_id`),
  UNIQUE KEY `invitee_inviter_idx` (`invitee_id`,`inviter_id`)
)

招待者 70 から招待者 62 への招待を選択したい、またはその逆を選択したい:

EXPLAIN SELECT * FROM `invitation` WHERE 
(invitee_id = 70 AND inviter_id = 62) OR (invitee_id = 62 AND inviter_id = 70)

ただし、このクエリはタイプが ALL であり、invitee_inviter_idx を使用しません。ここで何が間違っているのか教えてください。

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

==編集== 申し訳ありませんが、スキーマについて間違っていました。もう 1 つのフィールドがあります: request_ts。今回のクエリ プランは ALL です。

    CREATE TABLE `invitation` (
      `invitation_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
      `inviter_id` int(10) unsigned NOT NULL,
      `invitee_id` int(10) unsigned NOT NULL,
      `request_ts` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 
      PRIMARY KEY (`invitation_id`),
      UNIQUE KEY `invitee_inviter_idx` (`invitee_id`,`inviter_id`)
    )

これが私のexlain結果です:

id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   SIMPLE  invitation  ALL invitee_inviter_idx \N  \N      \N  1   Using where
4

2 に答える 2

10

選択がインデックスを使用していない理由が少なくとも 3 つあります。

1)select *インデックスにない項目を含む を使用しました (つまりinvitation_id)。つまり、インデックスを使用していた場合、データベース内の行を検索してinvitation_id値を取得する必要があります。インデックスに追加invitation_idすると、インデックスが使用されます。selectof justを実行した場合はinvitee_id, inviter_id、インデックスが使用されます。

2) クエリ オプティマイザは、インデックスの範囲をスキャンするよりもテーブルをスキャンする方がよいと判断しました。オプティマイザが全テーブル スキャンまたは部分インデックス スキャンを決定しようとしている場合、正確なクエリに対しては決定しません。一般的にうまく機能する計画が必要です。何度も何度も実行される可能性があるもの。invitee_id,inviter_id (62,70)~からスキャン中(70,62)おそらく 8 つのインデックス エントリのみですが、50,000 個のアイテムからランダムに選択された場合、平均距離は約 17,000 個のアイテムになります。したがって、平均して、単一のクエリはインデックスの 1/3 にアクセスし (つまり、メモリにプルします)、行が存在するページにアクセスし (#1 を参照)、それをメモリにプルします。行が非常に小さいため、1 つのアイテムにアクセスすると、テーブルの 1/70 である 680 行 (3 つの 32 ビット # の場合は 12 バイトの 8k ページ) が取り込まれる可能性があります。100 回のクエリを実行すると、インデックス全体をメモリとテーブル全体 - テーブルをスキャンするのに少し時間がかかり、他のテーブルのビットを保持するために 40% 少ないメモリを使用する方が理にかなっています。ある時点 (65k 行のようです) で意味がなくなります。

3) 質問の内容: OR を使用しました。OR 式を使用してインデックス内の何かを検索することはできません。つまり、62 または 70 を検索することはできません。代わりに、検索する範囲を生成し(62,70)、次にスキャンして取得します(70,62)(なぜこれができるのか #2 を参照)。悪くなる)。

あなたは「ここで何が問題なのか」と尋ねました-それは、スケーリングしないORを使用したことです。タイプ ALL を避ける必要があるだけでなく、大きなタイプ RANGES も避ける必要があります。

他の SQL エンジンでも同じ問題が発生しましたが、私が使用したソリューションは UNION ALL でした。

何かのようなもの

SELECT * FROM `invitation` WHERE 
    (invitee_id = 70 AND inviter_id = 62)
UNION ALL
SELECT  * FROM `invitation` WHERE
    (invitee_id = 62 AND inviter_id = 70)

これにより、2 つのクエリとして実行され、重複をチェックせずに結果がマージされます。

これは、メモリ使用量がはるかに軽く、はるかに高速です。インデックスの数ページとテーブルの 2 ページだけが必要で、ルックアップごとに O(log(N)) が必要です。これは、現在は const 型であるためです。目標は ALL を排除することでしたが、RANGE に切り替えることは、2 行だけを取得するのと同じくらい悪いことです。O(1/3*N) は O(N) であるため、テーブル全体のスキャンは O(N) であり、インデックスの RANGE のスキャンも O(N) です。つまり、スケーリングしません。

于 2012-12-15T16:46:53.760 に答える
3

テーブルに十分な行を取得するだけです。MySQL は十分に安価であるという理由だけで、小さなテーブルで完全なテーブル スキャンを実行します。

私の例では、65k 行をテーブルに入れ、インデックスを使用します。

http://sqlfiddle.com/#!2/63079/1

于 2012-12-15T17:45:05.660 に答える