2

質問があります:

select SQL_NO_CACHE id from users
 where id>1 and id <1000
   and id in  ( select owner_id from comments and content_type='Some_string');

(問題を表す、私のスフィンクスのインデックス作成に使用される実際の大きなクエリが不足していることに注意してください) このクエリには約3.5秒かかります(id = 1..5000から範囲を変更すると約15秒になります)。

ユーザーテーブルには約35000エントリがあり、コメントテーブルには約8000エントリがあります。

上記のクエリについて説明します。

explain select SQL_NO_CACHE id from users
        where id>1 and id <1000
          and id in  ( select distinct owner_id from d360_core_comments);

| id | select_type | テーブル| タイプ| possible_keys | キー| key_len | ref | 行| エクストラ|


| 1 | プライマリ| ユーザー| 範囲| プライマリ| プライマリ| 4 | NULL | 1992 | whereを使用する; インデックスの使用|

| 2 | 依存サブクエリ| d360_core_comments | すべて| NULL | NULL | NULL | NULL | 6901 | whereを使用する; 一時的な使用|

ここで、個々のsubquery(select owner_id from d360_core_comments where content_type='Community20::Topic';)はほぼ0.0秒かかります。

ただし、owner_id、content_typeにインデックスを追加すると、(ここでの順序に注意してください)

create index tmp_user on d360_core_comments (owner_id,content_type);

私のサブクエリは、インデックスを使用せずに約0.0秒でそのまま実行されます。

mysql> Explain select owner_id from d360_core_comments where content_type ='Community20 :: Topic';

| id | select_type | テーブル| タイプ| possible_keys | キー| key_len | ref | 行| エクストラ|


| 1 | シンプル| d360_core_comments | すべて| NULL | NULL | NULL | NULL | 6901 | whereを使用する|

しかし今、私のメインクエリ(select SQL_NO_CACHE id from users where id>1 and id <1000 and id in ( select owner_id from d360_core_comments where content_type='Community20::Topic');)は次の説明で〜0秒で実行されます:

mysql> Explain select SQL_NO_CACHE id from users where id> 1 and id <1000 and id in(select owner_id from d360_core_comments where content_type ='Community20 :: Topic');

| id | select_type | テーブル| タイプ| possible_keys | キー| key_len | ref | 行| エクストラ|


| 1 | プライマリ| ユーザー| 範囲| プライマリ| プライマリ| 4 | NULL | 1992 | whereを使用する; インデックスの使用|

| 2 | 依存サブクエリ| d360_core_comments | index_subquery | tmp_user | tmp_user | 5 | func | 34 | whereを使用する|

したがって、私が持っている主な質問は次のとおりです。

  • サブクエリで使用されるテーブルで定義されたインデックスが実際のサブクエリで使用されていない場合、ここでクエリをどのように最適化していますか?
  • そして、そもそも、実際のサブクエリとメインクエリが独立してはるかに高速であるのに、最初のクエリに非常に時間がかかったのはなぜですか?
4

4 に答える 4

3

インデックスのない完全なクエリで発生するように見えるのは、サブクエリが生成するすべての owner_id の (何らかの) 一時テーブルを MySQL が構築することです。次に、id 制約に一致するユーザー テーブルの各行に対して、この一時的な構成でルックアップが実行されます。オーバーヘッドが一時的な構成を作成しているのか、それともルックアップが最適に実装されていないのか (したがって、すべての要素が外部クエリからの各行に対して線形に一致するようになっているのか) は不明です。

owner_id でインデックスを作成する場合、サブクエリのみを実行しても何も変更されません。これは、owner_id に条件がなく、インデックスが content_type 列をカバーしていないためです。

ただし、インデックスを使用して完全なクエリを実行すると、より多くの情報が利用可能になります。これは、インデックスでカバーされている owner_id と一致する必要がある外部クエリからの値があるためです。そのため、外側のクエリの最初の部分を実行し、一致する行ごとに owner_id によるインデックス ルックアップを実行するようになりました。つまり、可能な実行計画は次のとおりです。

From Index-Users-Id Get all id matching id>1 and id <1000
For Each Row
    Include Row If Index-Comment-OwnerId Contains row.Id
                   And Row Matches content_type='Some_string'

したがって、この場合、1000 回の (私が推測する) インデックス ルックアップを実行する作業は、8000 回の可能性のある owner_id の一時的な構造を構築するよりも高速です。しかし、私は MySQL についてよく知らないので、これは仮説にすぎません。

于 2013-01-30T19:17:46.087 に答える
2

MySQL リファレンス マニュアルのこのセクション: Optimizing Subqueries with EXISTSStrategyを読むと、クエリ オプティマイザーがサブクエリ条件を次のように変換することがわかります。

id in ( select distinct owner_id
          from d360_core_comments
         where content_type='Community20::Topic')

の中へ:

exists ( select 1
           from d360_core_comments
          where content_type='Community20::Topic'
            and owner_id = users.id )

これが、サブクエリがスタンドアロン クエリとしてテストされるときにインデックス オン(owner_id, content_type)が役に立たない理由ですが、変換されたサブクエリを検討するときに役立ちます。

于 2013-02-01T18:15:00.343 に答える
1

最初に知っておくべきことは、MySQL は依存サブクエリを最適化できないということです。これは長い間よく知られている MySQL の欠陥であり、MySQL 6.x で修正される予定です (「mysql 依存サブクエリ」をググると、見ます)。つまり、サブクエリは基本的にusersテーブル内の一致する行ごとに実行されます。追加の条件があるため、全体の実行時間はその条件によって異なります。解決策は、サブクエリを結合に置き換えることです (MySQL に期待されるまさに最適化です)。

次に、サブクエリに構文エラーがあり、owner_id に条件があったと思います。したがって、インデックスを追加するowner_idと、使用されますが、2番目の条件には十分ではありません(したがって、いいえusing index)が、まったく言及されていない理由はEXPLAIN疑問です(の条件のためだと思いますusers.id

第 3 に、なぜそのid > 1 and id < 5000条件が必要なのかはわかりませんが、これらは 2 つの範囲条件であり、(等値比較条件とは対照的に) 非常に正確で、時には自明ではなく、データに依存するインデックス作成アプローチを必要とすることを理解する必要があります。それらを必要とせず、クエリに時間がかかる理由を理解するためだけに使用する場合、それは悪い考えであり、光を当てることはありません.

条件が必要で、インデックスowner_idがまだそこにある場合は、クエリを次のように書き直します。

SELECT id 
FROM (
  SELECT owner_id as id
  FROM comments
  WHERE owner_id < 5000 AND content_type = 'some_string'
) as ids
JOIN users ON (id)
WHERE id > 1;

PS の複合インデックス(content_type, owner_id)は、クエリに対してさらに優れています。

于 2013-02-01T11:30:41.103 に答える
0

ステップ1:id BETWEEN x AND yの代わりに使用しid >= x AND id <= yます。インデックスが改善されるため、驚くべきメリットが得られる場合があります。

ステップ2:サブSELECTを調整してフィルタリングを実行し、2回実行する必要がないようにします。

SELECT SQL_NO_CACHE id 
  FROM users
 WHERE id IN (SELECT owner_id 
                FROM comments
               WHERE content_type='Some_string' 
                 AND owner_id BETWEEN 1 AND 1000);

あなたの声明にはいくつかの誤りがあるようです。たとえば、2から999を選択していて、おそらく両端が1つずつずれていて、副選択が無効でした。

于 2013-01-28T18:08:05.223 に答える