これは、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
にインデックスを作成しないでください 削除されたエントリをめったに選択しない場合は、列。または、ベンチマークをニーズに合わせて調整し、独自の結論を出します。