web-dev-qa-db-ja.com

カーディナリティが低いインデックスを使用するのは理にかなっていますか?

私は主にActionscript開発者であり、SQLの専門家ではありませんが、単純なサーバー側のものを開発する必要がある場合があります。なので、タイトルの質問はもっと経験豊富な人に聞いてみようと思いました。

私の理解では、いくつかの異なる値を保持する列にインデックスを設定しても、あまり利益は得られません。ブール値を保持する列があり(実際には小さなintですが、フラグとして使用しています)、この列は、ほとんどのクエリのWHERE句で使用されています。理論上の「平均」の場合、レコードの値の半分は1で、残りの半分は0です。したがって、このシナリオでは、データベースエンジンは全表スキャンを回避できますが、とにかく多くの行を読み取る必要があります。 (合計行数/ 2)。

では、この列をインデックスにする必要がありますか?

記録のために、私はMysql 5を使用していますが、カーディナリティが低いことがわかっている列にインデックスを付けることが意味がある/意味がない理由についての一般的な理論的根拠にもっと興味があります。

前もって感謝します。

40

次の場合、インデックスはカーディナリティの低いフィールドでも役立ちます。

  1. 可能な値の1つが他の値と比較して非常にまれであり、それを検索する場合。

    たとえば、色覚異常の女性は非常に少ないため、次のクエリを実行します。

    SELECT  *
    FROM    color_blind_people
    WHERE   gender = 'F'
    

    おそらくgenderのインデックスから恩恵を受けるでしょう。

  2. 値がテーブル順にグループ化される傾向がある場合:

    SELECT  *
    FROM    records_from_2008
    WHERE   year = 2010
    LIMIT 1
    

    ここには3の異なる年しかありませんが、おそらく最初に古い年のレコードが追加されるため、インデックスがない場合は、最初の2010レコードを返す前に非常に多くのレコードをスキャンする必要があります。

  3. ORDER BY / LIMITが必要な場合:

    SELECT  *
    FROM    people
    ORDER BY
            gender, id
    LIMIT 1
    

    インデックスがないと、filesortが必要になります。 LIMITに対していくらか最適化されていますが、それでも全表スキャンが必要です。

  4. インデックスがクエリで使用されるすべてのフィールドをカバーする場合:

    CREATE INDEX (low_cardinality_record, value)
    
    SELECT  SUM(value)
    FROM    mytable
    WHERE   low_cardinality_record = 3
    
  5. DISTINCTが必要な場合:

    SELECT  DISTINCT color
    FROM    tshirts
    

    MySQLINDEX FOR GROUP-BYを使用します。色が少ない場合、このクエリは数百万のレコードがあっても即座に実行されます。

    これは、カーディナリティの低いフィールドのインデックスがカーディナリティの高いフィールドのインデックスよりもmore効率的であるシナリオの例です。

DMLのパフォーマンスがそれほど問題にならない場合は、インデックスを作成しても安全であることに注意してください。

オプティマイザーがインデックスが非効率的であると判断した場合、インデックスは使用されません。

78
Quassnoi

複合インデックスにブールフィールドを含めることは価値があるかもしれません。たとえば、通常は日付順に並べ替える必要があるメッセージの大きなテーブルがあるが、ブール値Deletedフィールドもある場合は、次のようにクエリを実行することがよくあります。

SELECT ... FROM Messages WHERE Deleted = 0 AND Date BETWEEN @start AND @end

削除済みフィールドと日付フィールドに複合インデックスを設定すると、間違いなくメリットがあります。

10
Vince Bowdren

私は通常、単純な「インデックスあり」と「ない」インデックステストを行います。私の経験では、インデックス付き列でORDERBYを使用するクエリでほとんどのパフォーマンスが得られます。その列に並べ替えがある場合は、インデックス作成が役立つ可能性があります。

3
Zepplock

レコードの値の半分が1になり、残りの半分が0になる場合、その列にインデックスを付ける意味はありません。クエリオプティマイザはそれを利用しない可能性があります。

ただし、通常は、「アクティブ」レコードの小さなセットと「非アクティブ」のセットがますます大きくなります。たとえば、バグ追跡システムでは、アクティブなバグに関心があり、完成してアーカイブされたバグをすべて見ることはほとんどありません。このような場合の秘訣は、レコードが非アクティブ化/削除されたときのタイムスタンプを格納する「dateInactivate」列を使用することです。名前が示すように、レコードがアクティブな間は値はNULLですが、非アクティブ化されたら、システムの日時に書き込みます。したがって、各レコードには一意の(厳密には言えない)値があるため、「削除された」レコードの数が増えると、その列のインデックスの選択性が高くなります。クエリには

"... AND dateInactivated is NULL ..." 

述語の一部として、インデックスはあなたが気にかけている行のちょうどいいセットを引き込みます。

2
user1454926

私見それは限られた有用性です。ほとんどの場合、フラグに加えて、クエリで使用している他の基準があり、おそらくもっと役立つと思います。

50%の場合、ベンチマークの有無にかかわらずベンチマークを実行し、それが大きな違いを生むかどうかを確認します。

2
Joe