sql >> データベース >  >> RDS >> Mysql

インデックスをカバーしているにもかかわらず、MySQL MyISAMの遅いcount()クエリ

    これが起こっていることです。

    The SELECT COUNT (...) icd_index where icd='25000'
    

    データとは別のBTreeであるインデックスを使用します。ただし、次のようにスキャンします:

    1. icd='25000'の最初のエントリを見つけます。これはほぼ瞬時です。
    2. icdに変化が見つかるまで、前方にスキャンします。これは、データに触れることなく、インデックスのみをスキャンします。 EXPLAINによると、スキャンするインデックスエントリは約910,104個あります。

    次に、そのインデックスのBTreeを見てみましょう。インデックスのフィールドに基づいて、各行は正確に22バイトになり、さらにオーバーヘッドが発生します(推定40%)。 MyISAMインデックスブロックは1KBです(InnoDBの16KBを参照)。ブロックあたり33行と推定します。 910,104 / 33は、COUNTを実行するには約27Kブロックを読み取る必要があると述べています。 (COUNT(core_id)に注意してください core_idを確認する必要があります nullの場合、COUNT(*) ではない;これは小さな違いです。)プレーンハードドライブで27Kブロックを読み取るには、約270秒かかります。 60秒で完了できてラッキーでした。

    2回目の実行では、key_buffer内のすべてのブロックが検出されたため(key_buffer_sizeが少なくとも27MBであると想定)、ディスクを待つ必要はありませんでした。したがって、それははるかに高速でした。 (これは、SQL_NO_CACHEをフラッシュまたは使用するための知恵を持っていたクエリキャッシュを無視します。)

    このプロセスは4.0以前から変更されていないため(utf8が存在しなかったことを除いて、以下で詳しく説明します)、5.6は無関係です(ただし、言及していただきありがとうございます)。

    InnoDBに切り替えると、いくつかの点で役立ちます。 PRIMARY KEYは、個別のBTreeとして保存されるのではなく、データとともに「クラスター化」されます。したがって、データまたはPKがキャッシュされると、もう一方はすぐに使用可能になります。ブロック数は5K程度になりますが、16KBブロックになります。キャッシュがコールドの場合、これらはより速くロードできる可能性があります。

    「icdだけでインデックスが必要ですか?」と質問します。これにより、MyISAM BTreeのサイズが1行あたり約21バイトに縮小されるため、BTreeのサイズは約21/27になり、あまり改善されません(少なくともコールドキャッシュの状況)。

    別の考えは、 if icd MEDIUMINT UNSIGNEDを使用するには、常に数値であり、常に数値です。 、ZEROFILLに取り組む 先行ゼロを持つことができる場合。

    おっと、文字セットに気づきませんでした。 (上記の数値を修正しましたが、詳しく説明します。)

    • CHAR(5)では5文字の
    • ASCIIは1バイトかかります 文字ごと
    • utf8は最大3バイトかかります 文字あたり
    • したがって、CHAR(5)CHARACTERSETutf8は15バイトかかります 常に

    列をCHAR(5) CHARACTER SET asciiに変更します 5バイトに縮小します。

    MEDIUMINT UNSIGNED ZEROFILLに変更すると、3バイトに縮小されます。

    データを縮小すると、I / Oがほぼ比例して高速化されます(他の2つのフィールドにさらに6バイトを許可した後。



    1. データベース設計:登録と検証

    2. Ruby on Railsアプリのmysqlデータベースをセットアップするときに、ソケット'/var/run/mysqld/mysqld.sock'エラーを介してローカルMySQLサーバーに接続できません

    3. MySQLのパーティショニングと結合

    4. 複数のフィールドを使用して別のテーブルの主キーを参照するMySQL外部キー