Berikut adalah tolok ukur MariaDB (10.0.19) dengan 10 juta baris (menggunakan plugin urutan ):
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;
Untuk mengukur waktu saya menggunakan set profiling=1
dan jalankan show profile
setelah menjalankan kueri. Dari hasil profileing saya ambil nilai Sending data
karena semuanya kurang dari satu mdtk.
TINYINT indeks:
SELECT COUNT(*) FROM test WHERE is_active = 1;
Waktu Proses:~ 738 mdtk
TIMESTAMP indeks:
SELECT COUNT(*) FROM test WHERE deleted_at is null;
Waktu Proses:~ 748 mdtk
Ukuran indeks:
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'
Hasil:
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
Perhatikan bahwa sementara TIMESTAMP (4 Byte) adalah 4 kali lebih panjang dari TYNYINT (1 Byte), ukuran indeks bahkan tidak dua kali lebih besar. Tetapi ukuran indeks bisa menjadi signifikan jika tidak sesuai dengan memori. Jadi ketika saya mengubah innodb_buffer_pool_size
dari 1G
ke 50M
saya mendapatkan nomor berikut:
- TINYINT:~ 960 mdtk
- TIMESTAMP:~ 1500 mdtk
Perbarui
Untuk menjawab pertanyaan secara lebih langsung, saya melakukan beberapa perubahan pada data:
- Alih-alih TIMESTAMP saya menggunakan DATETIME
- Karena entri biasanya jarang dihapus, saya menggunakan
rand(1)<0.99
(1% dihapus) alih-alihrand(1)<0.5
(50% dihapus) - Ukuran tabel diubah dari 10 juta menjadi 1 juta baris.
SELECT COUNT(*)
diubah menjadiSELECT *
Ukuran indeks:
index_name | stat_value*@@innodb_page_size
------------------------------------------
PRIMARY | 25739264
deleted_at | 12075008
is_active | 11026432
Sejak 99% dari deleted_at
nilainya NULL tidak ada perbedaan signifikan dalam ukuran indeks, meskipun DATETIME yang tidak kosong membutuhkan 8 Bytes (MariaDB).
SELECT * FROM test WHERE is_active = 1; -- 782 msec
SELECT * FROM test WHERE deleted_at is null; -- 829 msec
Menjatuhkan kedua indeks, kedua kueri dieksekusi dalam waktu sekitar 350 mdtk. Dan menghapus is_active
kolom deleted_at is null
kueri dijalankan dalam 280 mdtk.
Perhatikan bahwa ini masih bukan skenario yang realistis. Anda mungkin tidak ingin memilih 990 ribu baris dari 1 juta dan mengirimkannya ke pengguna. Anda mungkin juga akan memiliki lebih banyak kolom (mungkin termasuk teks) dalam tabel. Tetapi ini menunjukkan, bahwa Anda mungkin tidak memerlukan is_active
kolom (jika tidak menambahkan informasi tambahan), dan bahwa indeks apa pun dalam kasus terbaik tidak berguna untuk memilih entri yang tidak dihapus.
Namun indeks dapat berguna untuk memilih baris yang dihapus:
SELECT * FROM test WHERE is_active = 0;
Dieksekusi dalam 10 mdtk dengan indeks dan dalam 170 mdtk tanpa indeks.
SELECT * FROM test WHERE deleted_at is not null;
Dieksekusi dalam 11 mdtk dengan indeks dan dalam 167 mdtk tanpa indeks.
Menjatuhkan is_active
kolom itu dijalankan dalam 4 mdtk dengan indeks dan dalam 150 mdtk tanpa indeks.
Jadi, jika skenario ini cocok dengan data Anda, kesimpulannya adalah:Lepaskan is_active
kolom dan jangan buat indeks pada deleted_at
kolom jika Anda jarang memilih entri yang dihapus. Atau sesuaikan tolok ukur dengan kebutuhan Anda dan buat kesimpulan sendiri.