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

インデックス付きブール列と日時列のクエリのパフォーマンス

    これは、1,000万行のMariaDB(10.0.19)ベンチマークです(シーケンスプラグインを使用 ):

    drop table if exists test;
    CREATE TABLE `test` (
        `id` MEDIUMINT UNSIGNED NOT NULL,
        `is_active` TINYINT UNSIGNED NOT NULL,
        `deleted_at` TIMESTAMP NULL,
        PRIMARY KEY (`id`),
        INDEX `is_active` (`is_active`),
        INDEX `deleted_at` (`deleted_at`)
    ) ENGINE=InnoDB
        select seq id
            , rand(1)<0.5 as is_active
            , case when rand(1)<0.5 
                then null
                else '2017-03-18' - interval floor(rand(2)*1000000) second
            end as deleted_at
        from seq_1_to_10000000;
    

    時間を測定するには、set profiling=1を使用します show profileを実行します クエリを実行した後。プロファイリングの結果から、Sending dataの値を取得します 他のすべては完全に1ミリ秒未満なので。

    TINYINT インデックス:

    SELECT COUNT(*) FROM test WHERE is_active = 1;
    

    ランタイム:〜738ミリ秒

    タイムスタンプ インデックス:

    SELECT COUNT(*) FROM test WHERE  deleted_at is null;
    

    ランタイム:〜748ミリ秒

    インデックスサイズ:

    select database_name, table_name, index_name, stat_value*@@innodb_page_size
    from mysql.innodb_index_stats 
    where database_name = 'tmp'
      and table_name = 'test'
      and stat_name = 'size'
    

    結果:

    database_name | table_name | index_name | stat_value*@@innodb_page_size
    -----------------------------------------------------------------------
    tmp           | test       | PRIMARY    | 275513344 
    tmp           | test       | deleted_at | 170639360 
    tmp           | test       | is_active  |  97107968 
    

    TIMESTAMP(4バイト)はTYNYINT(1バイト)の4倍の長さですが、インデックスサイズは2倍にもならないことに注意してください。ただし、インデックスサイズがメモリに収まらない場合は、インデックスサイズが大きくなる可能性があります。したがって、innodb_buffer_pool_sizeを変更すると 1Gから 50Mへ 次の番号が表示されます:

    • TINYINT:〜960ミリ秒
    • タイムスタンプ:〜1500ミリ秒

    更新

    質問に直接対処するために、データにいくつかの変更を加えました:

    • TIMESTAMPの代わりにDATETIMEを使用します
    • 通常、エントリが削除されることはめったにないため、rand(1)<0.99を使用します。 (1%削除)rand(1)<0.5の代わりに (50%削除)
    • テーブルサイズが10Mから1M行に変更されました。
    • SELECT COUNT(*) SELECT *に変更されました

    インデックスサイズ:

    index_name | stat_value*@@innodb_page_size
    ------------------------------------------
    PRIMARY    | 25739264
    deleted_at | 12075008
    is_active  | 11026432
    

    deleted_atの99%以降 値がNULLの場合、インデックスサイズに大きな違いはありませんが、空でないDATETIMEには8バイト(MariaDB)が必要です。

    SELECT * FROM test WHERE is_active = 1;      -- 782 msec
    SELECT * FROM test WHERE deleted_at is null; -- 829 msec
    

    両方のインデックスを削除すると、両方のクエリが約350ミリ秒で実行されます。そして、is_activeを削除します 列deleted_at is null クエリは280ミリ秒で実行されます。

    これはまだ現実的なシナリオではないことに注意してください。 1Mから990K行を選択して、ユーザーに配信することはほとんどありません。おそらく、テーブルにはさらに多くの列(おそらくテキストを含む)があります。しかし、それはおそらくis_activeは必要ないことを示しています 列(追加情報が追加されていない場合)、および削除されていないエントリを選択するのに、どのインデックスもほとんどの場合役に立たないこと。

    ただし、インデックスは削除された行を選択するのに役立ちます:

    SELECT * FROM test WHERE is_active = 0;
    

    インデックスありで10ミリ秒、インデックスなしで170ミリ秒で実行されます。

    SELECT * FROM test WHERE deleted_at is not null;
    

    インデックスありで11ミリ秒、インデックスなしで167ミリ秒で実行されます。

    is_activeを削除する 列は、インデックスありで4ミリ秒、インデックスなしで150ミリ秒で実行されます。

    したがって、このシナリオがデータに何らかの形で適合する場合、結論は次のようになります。is_activeを削除します。 列であり、deleted_atにインデックスを作成しないでください 削除されたエントリをめったに選択しない場合は、列。または、ベンチマークをニーズに合わせて調整し、独自の結論を出します。



    1. CSVをMySQLにインポートし、日付を変換します

    2. MySQLWHERE句の現在の日付

    3. MySQLでのラグ関数のシミュレーション

    4. SailsJSとmySQLのカスタムID名がブループリントで機能しない