複合インデックス:最も選択的な列が最初ですか?


17

私は読んでいてcomposite indexes、注文について少し混乱しています。このドキュメント(半分以下)が言います

一般に、最も頻繁に使用されると予想される列をインデックスの最初に配置する必要があります。

しかし、それが言った直後

最も選択的な列を最初に置く複合インデックスを作成します。つまり、値が最も多い列です。

オラクルはまた、ここで言い換えると

すべてのキーがWHERE句で同じ頻度で使用される場合、CREATE INDEXステートメントでこれらのキーを最も選択性の高いものから最も選択性の低いものに並べると、クエリのパフォーマンスが向上します。

しかし、私は違う答えをするSOの答えを見つけました。それは言う

列を最も選択性の低い列を最初に、最も選択性の高い列を最後に配置します。単独で使用される可能性が高いカラムとのタイリードの場合。

私が参照した最初のドキュメントは、最も頻繁に使用されるものを最初に確認する必要があると述べていますが、SOの答えは、タイブレークのみであるべきだと述べています。次に、順序も異なります。

このドキュメントはまた語るskip scanningと言います

スキップスキャンは、複合インデックスの先頭列に個別の値がほとんどなく、インデックスの先頭以外のキーに個別の値が多数ある場合に有利です。

別の記事は言う

プレフィックス列は、クエリで最も識別性が高く、最も広く使用される必要があります

最も差別的であることは最も特徴的だと思います。

この研究はすべて、私に同じ質問を導きます。最も選択的な列は最初か最後か?最初の列は、タイブレークで最も使用され、最も選択的である必要がありますか?

これらの記事は互いに矛盾しているように見えますが、いくつかの例を提供しています。私が集めたものから、もしあなたが予想しているなら、注文least selective column最初のものであることがより効率的であるように思われますIndex Skip Scans。しかし、それが正しいかどうかはよくわかりません。


回答:


8

AskTomから

(9iには、新しい「インデックススキップスキャン」があります。それを検索して、それについて読んでください。インデックス(a、b)OR(b、a)は、上記の両方の場合に役立つことがあります!)

したがって、インデックス内の列の順序は、クエリの記述方法によって異なります。できる限り多くのクエリにインデックスを使用できるようにしたい場合(つまり、所有するすべてのインデックスを削減するため)、列の順序を決定します。他には何もありません(aまたはbの選択性はまったく考慮されません)。

複合インデックス内の列を最小の差別化(より明確な値)から最も差別的(より明確な値)に並べるための引数の1つは、インデックスキーの圧縮です。

SQL> create table t as select * from all_objects;

Table created.

SQL> create index t_idx_1 on t(owner,object_type,object_name);

Index created.

SQL> create index t_idx_2 on t(object_name,object_type,owner);

Index created.

SQL> select count(distinct owner), count(distinct object_type), count(distinct object_name ), count(*)  from t;

COUNT(DISTINCTOWNER) COUNT(DISTINCTOBJECT_TYPE) COUNT(DISTINCTOBJECT_NAME)      COUNT(*)
-------------------- -------------------------- --------------------------      ----------
                 30                         45                       52205      89807

SQL> analyze index t_idx_1 validate structure; 

Index analyzed.

SQL> select btree_space, pct_used, opt_cmpr_count, opt_cmpr_pctsave from index_stats;

BTREE_SPACE   PCT_USED OPT_CMPR_COUNT OPT_CMPR_PCTSAVE
----------- ---------- -------------- ----------------
    5085584     90          2           28

SQL> analyze index t_idx_2 validate structure; 

Index analyzed.

SQL> select btree_space, pct_used, opt_cmpr_count, opt_cmpr_pctsave  from index_stats; 

BTREE_SPACE   PCT_USED OPT_CMPR_COUNT OPT_CMPR_PCTSAVE
----------- ---------- -------------- ----------------
    5085584     90          1           14

インデックス統計によると、最初のインデックスはより圧縮可能です。

もう1つは、クエリでのインデックスの使用方法です。クエリが主に使用する場合col1

たとえば、次のようなクエリがある場合

  • select * from t where col1 = :a and col2 = :b;
  • select * from t where col1 = :a;

    -その後index(col1,col2)、パフォーマンスが向上します。

    クエリが主に使用する場合col2

  • select * from t where col1 = :a and col2 = :b;
  • select * from t where col2 = :b;

    -その後index(col2,col1)、パフォーマンスが向上します。すべてのクエリが常に両方の列を指定する場合、複合インデックスのどちらの列が最初に来るかは関係ありません。

    結論として、複合インデックスの列の順序に関する重要な考慮事項は、インデックスキーの圧縮と、クエリでこのインデックスをどのように使用するかです。

    参照:

  • インデックスの列順
  • インデックスにカーディナリティの低い先行列があると効率が低下します(右)。
  • インデックススキップスキャン-インデックス列の順序は問題になりますか?(警告サイン)


  • 3

    最も選択的な最初は、この列が実際のWHERE句にある場合にのみ役立ちます。

    SELECTがより大きなグループ(選択性が低い)であり、場合によっては他のインデックス化されていない値でもある場合、選択性の低い列を持つインデックスは依然として有用です(別の列を作成しない理由がある場合)。

    テーブルADDRESSがある場合、

    COUNTRY CITY STREET、何か他の...

    STREET、CITY、COUNTRYのインデックスを作成すると、通りの名前で最速のクエリが生成されます。ただし、都市のすべての道路を照会すると、インデックスは役に立たなくなり、クエリはテーブル全体をスキャンする可能性があります。

    COUNTRY、CITY、STREETのインデックス作成は個々の道路では少し遅くなる場合がありますが、国や都市でのみ選択する他のクエリにはインデックスを使用できます。


    3

    インデックス列の順序を選択するとき、最も重要なことは次のとおりです。

    クエリにこの列に対する(平等)述語がありますか?

    列がwhere句に表示されない場合、indexing(1)の価値はありません

    OK、それでテーブルと各列に対するクエリができました。時には複数。

    インデックスの対象をどのように決定しますか?

    例を見てみましょう。これは3列の表です。1つは10個の値を保持し、もう1つは最後の10,000個を保持します。

    create table t(
      few_vals  varchar2(10),
      many_vals varchar2(10),
      lots_vals varchar2(10)
    );
    
    insert into t 
    with rws as (
      select lpad(mod(rownum, 10), 10, '0'), 
             lpad(mod(rownum, 1000), 10, '0'), 
             lpad(rownum, 10, '0') 
      from dual connect by level <= 10000
    )
      select * from rws;
    
    commit;
    
    select count(distinct few_vals),
           count(distinct many_vals) ,
           count(distinct lots_vals) 
    from   t;
    
    COUNT(DISTINCTFEW_VALS)  COUNT(DISTINCTMANY_VALS)  COUNT(DISTINCTLOTS_VALS)  
    10                       1,000                     10,000     

    これらは、ゼロが埋め込まれた数値です。これは、後で圧縮についてのポイントを作るのに役立ちます。

    したがって、3つの一般的なクエリがあります。

    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  few_vals = '0000000001';
    
    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  lots_vals = '0000000001';
    
    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  lots_vals = '0000000001'
    and    few_vals = '0000000001';

    何をインデックスしますか?

    数個のvalsのインデックスは、テーブル全体のスキャンよりもわずかに優れています。

    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  few_vals = '0000000001';
    
    select * 
    from table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    -------------------------------------------------------------------------------------------  
    | Id  | Operation            | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  
    -------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT     |          |      1 |        |      1 |00:00:00.01 |      61 |  
    |   1 |  SORT AGGREGATE      |          |      1 |      1 |      1 |00:00:00.01 |      61 |  
    |   2 |   VIEW               | VW_DAG_0 |      1 |   1000 |   1000 |00:00:00.01 |      61 |  
    |   3 |    HASH GROUP BY     |          |      1 |   1000 |   1000 |00:00:00.01 |      61 |  
    |   4 |     TABLE ACCESS FULL| T        |      1 |   1000 |   1000 |00:00:00.01 |      61 |  
    -------------------------------------------------------------------------------------------
    
    select /*+ index (t (few_vals)) */
           count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  few_vals = '0000000001';
    
    select * 
    from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    -------------------------------------------------------------------------------------------------------------  
    | Id  | Operation                              | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  
    -------------------------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT                       |          |      1 |        |      1 |00:00:00.01 |      58 |  
    |   1 |  SORT AGGREGATE                        |          |      1 |      1 |      1 |00:00:00.01 |      58 |  
    |   2 |   VIEW                                 | VW_DAG_0 |      1 |   1000 |   1000 |00:00:00.01 |      58 |  
    |   3 |    HASH GROUP BY                       |          |      1 |   1000 |   1000 |00:00:00.01 |      58 |  
    |   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| T        |      1 |   1000 |   1000 |00:00:00.01 |      58 |  
    |   5 |      INDEX RANGE SCAN                  | FEW      |      1 |   1000 |   1000 |00:00:00.01 |       5 |  
    -------------------------------------------------------------------------------------------------------------

    したがって、単独でインデックスを作成する価値はありません。lots_valsのクエリは、数行(この場合は1行のみ)を返します。したがって、これは間違いなく索引付けの価値があります。

    しかし、両方の列に対するクエリはどうでしょうか?

    インデックスを作成する必要があります:

    ( few_vals, lots_vals )

    または

    ( lots_vals, few_vals )

    ひっかけ質問!

    答えはどちらでもありません。

    確かに、fight_valsは長い文字列です。したがって、適切な圧縮を行うことができます。そして、(few_vals、lots_vals)を使用して、lots_valsにのみ述語を持つクエリのインデックススキップスキャンを取得する可能性があります。ただし、フルスキャンよりも大幅に優れたパフォーマンスを発揮しますが、ここにはありません。

    create index few_lots on t(few_vals, lots_vals);
    
    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  lots_vals = '0000000001';
    
    select * 
    from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    -------------------------------------------------------------------------------------------  
    | Id  | Operation            | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  
    -------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT     |          |      1 |        |      1 |00:00:00.01 |      61 |  
    |   1 |  SORT AGGREGATE      |          |      1 |      1 |      1 |00:00:00.01 |      61 |  
    |   2 |   VIEW               | VW_DAG_0 |      1 |      1 |      1 |00:00:00.01 |      61 |  
    |   3 |    HASH GROUP BY     |          |      1 |      1 |      1 |00:00:00.01 |      61 |  
    |   4 |     TABLE ACCESS FULL| T        |      1 |      1 |      1 |00:00:00.01 |      61 |  
    -------------------------------------------------------------------------------------------  
    
    select /*+ index_ss (t few_lots) */count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  lots_vals = '0000000001';
    
    select * 
    from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    ----------------------------------------------------------------------------------------------------------------------  
    | Id  | Operation                              | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads  |  
    ----------------------------------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT                       |          |      1 |        |      1 |00:00:00.01 |      13 |     11 |  
    |   1 |  SORT AGGREGATE                        |          |      1 |      1 |      1 |00:00:00.01 |      13 |     11 |  
    |   2 |   VIEW                                 | VW_DAG_0 |      1 |      1 |      1 |00:00:00.01 |      13 |     11 |  
    |   3 |    HASH GROUP BY                       |          |      1 |      1 |      1 |00:00:00.01 |      13 |     11 |  
    |   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| T        |      1 |      1 |      1 |00:00:00.01 |      13 |     11 |  
    |   5 |      INDEX SKIP SCAN                   | FEW_LOTS |      1 |     40 |      1 |00:00:00.01 |      12 |     11 |  
    ----------------------------------------------------------------------------------------------------------------------

    ギャンブルが好きですか?(2)

    したがって、先行列としてlots_valsを持つインデックスが必要です。そして、少なくともこの場合、複合インデックス(少数、ロット)は、ちょうど(ロット)に対するものと同じ量の作業を行います。

    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  lots_vals = '0000000001'
    and    few_vals = '0000000001';
    
    select * 
    from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    -------------------------------------------------------------------------------------------------------------  
    | Id  | Operation                              | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  
    -------------------------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT                       |          |      1 |        |      1 |00:00:00.01 |       3 |  
    |   1 |  SORT AGGREGATE                        |          |      1 |      1 |      1 |00:00:00.01 |       3 |  
    |   2 |   VIEW                                 | VW_DAG_0 |      1 |      1 |      1 |00:00:00.01 |       3 |  
    |   3 |    HASH GROUP BY                       |          |      1 |      1 |      1 |00:00:00.01 |       3 |  
    |   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| T        |      1 |      1 |      1 |00:00:00.01 |       3 |  
    |   5 |      INDEX RANGE SCAN                  | FEW_LOTS |      1 |      1 |      1 |00:00:00.01 |       2 |  
    -------------------------------------------------------------------------------------------------------------  
    
    create index lots on t(lots_vals);
    
    select /*+ index (t (lots_vals)) */count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  lots_vals = '0000000001'
    and    few_vals = '0000000001';
    
    select * 
    from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    ----------------------------------------------------------------------------------------------------------------------  
    | Id  | Operation                              | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads  |  
    ----------------------------------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT                       |          |      1 |        |      1 |00:00:00.01 |       3 |      1 |  
    |   1 |  SORT AGGREGATE                        |          |      1 |      1 |      1 |00:00:00.01 |       3 |      1 |  
    |   2 |   VIEW                                 | VW_DAG_0 |      1 |      1 |      1 |00:00:00.01 |       3 |      1 |  
    |   3 |    HASH GROUP BY                       |          |      1 |      1 |      1 |00:00:00.01 |       3 |      1 |  
    |   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| T        |      1 |      1 |      1 |00:00:00.01 |       3 |      1 |  
    |   5 |      INDEX RANGE SCAN                  | LOTS     |      1 |      1 |      1 |00:00:00.01 |       2 |      1 |  
    ----------------------------------------------------------------------------------------------------------------------  

    複合インデックスが1〜2 IOを節約する場合があります。しかし、この節約のために2つのインデックスを持つ価値はありますか?

    また、複合インデックスには別の問題があります。LOTS_VALSを含む3つのインデックスのクラスタリング係数を比較します。

    create index lots on t(lots_vals);
    create index lots_few on t(lots_vals, few_vals);
    create index few_lots on t(few_vals, lots_vals);
    
    select index_name, leaf_blocks, distinct_keys, clustering_factor
    from   user_indexes
    where  table_name = 'T';
    
    INDEX_NAME  LEAF_BLOCKS  DISTINCT_KEYS  CLUSTERING_FACTOR  
    FEW_LOTS    47           10,000         530                
    LOTS_FEW    47           10,000         53                 
    LOTS        31           10,000         53                 
    FEW         31           10             530    

    rare_lotsのクラスタリング係数は、lotsおよびlots_few より10倍高いことに注意してください!そして、これは最初から完璧なクラスタリングを備えたデモ表にあります。実際のデータベースでは、影響はさらに大きくなる可能性があります。

    それで何がそんなに悪いのでしょうか?

    クラスタリング係数は、インデックスがどの程度「魅力的」であるかを決定する重要な要因の1つです。値が高いほど、オプティマイザが選択する可能性は低くなります。特に、lots_valsが実際には一意ではない場合でも、通常は値ごとに数行しかありません。運が悪い場合、これはオプティマイザにフルスキャンの方が安いと思わせるのに十分かもしれません...

    わかりました。したがって、fewy_valsとlots_valsを使用した複合インデックスには、エッジケースの利点しかありません。

    少数の値と多数の値をフィルタリングするクエリはどうですか?

    単一列インデックスには、わずかな利点しかありません。しかし、それらを組み合わせると、ほとんど値を返しません。そのため、複合インデックスは良いアイデアです。しかし、どっちの道?

    最初にいくつかを配置する場合、先頭の列を圧縮すると、それが小さくなります

    create index few_many on t(many_vals, few_vals);
    create index many_few on t(few_vals, many_vals);
    
    select index_name, leaf_blocks, distinct_keys, clustering_factor 
    from   user_indexes
    where  index_name in ('FEW_MANY', 'MANY_FEW');
    
    INDEX_NAME  LEAF_BLOCKS  DISTINCT_KEYS  CLUSTERING_FACTOR  
    FEW_MANY    47           1,000          10,000             
    MANY_FEW    47           1,000          10,000   
    
    alter index few_many rebuild compress 1;
    alter index many_few rebuild compress 1;
    
    select index_name, leaf_blocks, distinct_keys, clustering_factor 
    from   user_indexes
    where  index_name in ('FEW_MANY', 'MANY_FEW');
    
    INDEX_NAME  LEAF_BLOCKS  DISTINCT_KEYS  CLUSTERING_FACTOR  
    MANY_FEW    31           1,000          10,000             
    FEW_MANY    34           1,000          10,000      

    先行列の値が少ないほど、圧縮率が高くなります。したがって、このインデックスを読み取る作業はわずかに少なくなります。しかし、ほんのわずか。そして、両方ともすでに元のサイズよりも小さい適切なチャンクです(サイズが25%減少)。

    さらに、さらに進んで、インデックス全体を圧縮できます!

    alter index few_many rebuild compress 2;
    alter index many_few rebuild compress 2;
    
    select index_name, leaf_blocks, distinct_keys, clustering_factor 
    from   user_indexes
    where  index_name in ('FEW_MANY', 'MANY_FEW');
    
    INDEX_NAME  LEAF_BLOCKS  DISTINCT_KEYS  CLUSTERING_FACTOR  
    FEW_MANY    20           1,000          10,000             
    MANY_FEW    20           1,000          10,000   

    これで、両方のインデックスが同じサイズに戻りました。これは、少数と多数の間に関係があるという事実を利用していることに注意してください。繰り返しますが、現実の世界でこの種の利点を目にすることはまずありません。

    ここまでは、等価チェックについてのみ説明しました。多くの場合、複合インデックスを使用すると、いずれかの列に対して不平等が生じます。たとえば、「過去N日間の顧客の注文/発送/請求書を取得する」などのクエリ。

    これらの種類のクエリがある場合は、インデックスの最初の列に対する同等性が必要です。

    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  few_vals < '0000000002'
    and    many_vals = '0000000001';
    
    select * 
    from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    -------------------------------------------------------------------------------------------------------------  
    | Id  | Operation                              | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  
    -------------------------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT                       |          |      1 |        |      1 |00:00:00.01 |      12 |  
    |   1 |  SORT AGGREGATE                        |          |      1 |      1 |      1 |00:00:00.01 |      12 |  
    |   2 |   VIEW                                 | VW_DAG_0 |      1 |     10 |     10 |00:00:00.01 |      12 |  
    |   3 |    HASH GROUP BY                       |          |      1 |     10 |     10 |00:00:00.01 |      12 |  
    |   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| T        |      1 |     10 |     10 |00:00:00.01 |      12 |  
    |   5 |      INDEX RANGE SCAN                  | FEW_MANY |      1 |     10 |     10 |00:00:00.01 |       2 |  
    -------------------------------------------------------------------------------------------------------------  
    
    select count (distinct few_vals || ':' || many_vals || ':' || lots_vals )
    from   t
    where  few_vals = '0000000001'
    and    many_vals < '0000000002';
    
    select * 
    from   table(dbms_xplan.display_cursor(null, null, 'IOSTATS LAST -PREDICATE'));
    
    ----------------------------------------------------------------------------------------------------------------------  
    | Id  | Operation                              | Name     | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads  |  
    ----------------------------------------------------------------------------------------------------------------------  
    |   0 | SELECT STATEMENT                       |          |      1 |        |      1 |00:00:00.01 |      12 |      1 |  
    |   1 |  SORT AGGREGATE                        |          |      1 |      1 |      1 |00:00:00.01 |      12 |      1 |  
    |   2 |   VIEW                                 | VW_DAG_0 |      1 |      2 |     10 |00:00:00.01 |      12 |      1 |  
    |   3 |    HASH GROUP BY                       |          |      1 |      2 |     10 |00:00:00.01 |      12 |      1 |  
    |   4 |     TABLE ACCESS BY INDEX ROWID BATCHED| T        |      1 |      2 |     10 |00:00:00.01 |      12 |      1 |  
    |   5 |      INDEX RANGE SCAN                  | MANY_FEW |      1 |      1 |     10 |00:00:00.01 |       2 |      1 |  
    ----------------------------------------------------------------------------------------------------------------------  

    反対のインデックスを使用していることに注意してください。

    TL; DR

    • 等価条件のある列は、インデックスの最初に配置する必要があります。
    • クエリに等しい列が複数ある場合は、値が最も少ない列を最初に配置すると、圧縮の利点が最大になります
    • インデックススキップスキャンは可能ですが、これが近い将来に実行可能なオプションであり続けることを確信する必要があります。
    • ほぼ一意の列を含む複合インデックスの利点はほとんどありません。必ず1-2 IOを保存する必要があることを確認してください

    1:場合によっては、クエリのすべての列がインデックス内にあることを意味する場合、インデックスに列を含める価値があるかもしれません。これにより、インデックスのみのスキャンが可能になるため、テーブルにアクセスする必要はありません。

    2:Diagnostics and Tuningのライセンスを取得している場合、SQL Plan Managementを使用して計画を強制的にスキップスキャンできます。

    ADDEDNDA

    PS-あなたが引用したドキュメントは9iからです。それは非常に古いです。私はもっと最近のものに固執します


    クエリはselect count (distinct few_vals || ':' || many_vals || ':' || lots_vals )本当に一般的ですか?Oracleは構文を許可しselect count (distinct few_vals, many_vals, lots_vals )ません-文字列連結を行わず、列がテキストタイプである必要はなく、:文字の不在に依存しませんか?
    ypercubeᵀᴹ

    @ypercubeᵀᴹOracleではできませんcount ( distinct x, y, z )。したがって、個別のサブクエリを実行し、結果または上記のような連結をカウントする必要があります。私はちょうどテーブルアクセス(だけではなくスキャンインデックス)を強制するために、ここでそれをやっただけの結果に1行を持っている
    クリス・サクソン

    1

    複合インデックスが列の選択性に加えて開始および/または含まれるべきものに関する最終決定に寄与するクエリの要素がさらにあります。

    例えば:

    1. 使用されているクエリ演算子の種類:クエリに
      「>、> =、<、<= "などの演算子がある場合
    2. クエリの結果として予想される実際の行数:クエリ結果はテーブルのほとんどの行になりますか。
    3. WHERE句中にテーブル列で関数が使用されていますか:WHERE条件で使用されている列で使用されている関数UPPER、LOWER、TRIM、SUBSTRINGがクエリにある場合。

    まだ関連する会話を維持するために、以下の答えは次の状況に適用されます:

    1. 「特定のテーブルのクエリの90%タイプには、演算子=のWHERE句があります」
    2. 「ほとんどのクエリは結果としてテーブルの合計行の10%を返します」
    3. 「WHERE句のテーブル列で使用されている関数はありません」
    4. 「使用されるWHERE句のほとんどの時間列は、ほとんどが型番号、
      文字列です」

    私の経験では、DBAが留意すべきことは両方です。

    唯一のルールが適用されていると想像してみましょう。

    1)最も選択的な列を最初に使用してインデックスを作成しますが、その列は実際にはそのテーブルのほとんどのクエリで使用されないため、dbエンジンでは使用されません。

    2)クエリで最も広く使用されている列がインデックスの最初にあるインデックスを作成しますが、列の選択性が低い場合、クエリのパフォーマンスが低下します。

    テーブルクエリの90%で主に使用される列をリストします。次に、それらをカーディナリティが最も大きいものから最も少ないものの順に並べます。

    読み取りクエリのパフォーマンスを改善するためにインデックスを使用し、そのワークフロー(読み取りクエリのタイプ)のみがインデックス作成を駆動します。実際、データが大きくなると(数十億行)、圧縮されたインデックスによってストレージは節約されますが、読み取りクエリのパフォーマンスは必ず低下します。


    1

    理論的には、最も選択的な列が最速の検索をもたらします。しかし、職場では、3つの部分の複合インデックスがあり、最初に最も選択的な部分があるという状況に出くわしました。(日付、著者、出版社は、この順序で、テーブルが投稿の親指を立てることができます)、3つの部分すべてを使用するクエリがあります。Mysqlはデフォルトで、著者のonlnyインデックスを使用します。これは、クエリに存在しているにもかかわらず、会社と日付を含む複合インデックスをスキップします。コンポジットを使用するために強制インデックスを使用しましたが、実際にはクエリの実行速度が遅くなりました。なぜそうなったのですか?教えてあげましょう:

    私は日付の範囲を選択していたので、日付が非常に選択的であるにもかかわらず、範囲スキャンに使用しているという事実(範囲は比較的短くても、6年のデータのうち6ヶ月)が複合に有害でしたmysql。その特定のケースでコンポジットを使用するには、mysqlは新しい年以降に書かれたすべての記事を取得してから著者を詳しく調べなければなりません。 。

    別のケースでは、クエリがコンポジットではるかに高速に実行されました。ケースは、著者が非常に人気があり、ほとんどのレコードを所有していて、日付でソートすることが理にかなっている場合でした。しかし、mysqlはそのようなケースを自動検出しなかったため、インデックスを強制する必要がありました。範囲スキャンにより、選択した列が役に立たなくなる可能性があります。データの分布により、列が異なるレコードに対してより選択的である場合があります...

    私が異なる方法で行うのは、日付を右にシフトすることです(これも理論的には最も選択的です)。これで範囲スキャンを実行することを知っているので、違いが生じます。


    1
    クエリに次のようなものがあるWHERE (date BETWEEN @x AND @y) AND (author = @a) AND (publishing company = @p)場合、インデックスを強制的に使用せずに、オン(author, publishing_company, date)またはオンのインデックスを(publishing_company, author, date)使用することをお勧めします。
    ypercubeᵀᴹ

    -2

    さまざまな状況のさまざまなケース。あなたの目標を知ってください。次に、インデックスを作成し、それぞれについて説明計画を実行すると、状況に応じて最善の答えが得られます。


    -2

    Ask Tomのインデックスの列順から:

    したがって、インデックス内の列の順序は、クエリの記述方法によって異なります。できる限り多くのクエリにインデックスを使用できるようにしたい場合(つまり、所有するすべてのインデックスを削減するため)、列の順序を決定します。他には何もありません(aまたはbの選択性はまったく考慮されません)。

    where句に基づいて列を並べる必要があることに同意しますが、「(aまたはbの選択性はまったくカウントされません)」という文は正しくありません。)。 (「where句」)

    弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
    Licensed under cc by-sa 3.0 with attribution required.