Ecco un benchmark MariaDB (10.0.19) con 10 milioni di righe (utilizzando il plug-in sequenza ):
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;
Per misurare il tempo utilizzo set profiling=1
ed esegui show profile
dopo aver eseguito una query. Dal risultato della profilazione prendo il valore di Sending data
poiché tutto il resto è complessivamente inferiore a un msec.
TINYINT indice:
SELECT COUNT(*) FROM test WHERE is_active = 1;
Durata:~ 738 msec
TIMESTAMP indice:
SELECT COUNT(*) FROM test WHERE deleted_at is null;
Durata:~ 748 msec
Dimensione dell'indice:
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'
Risultato:
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
Si noti che mentre TIMESTAMP (4 byte) è 4 volte più lungo di TYNYINT (1 byte), la dimensione dell'indice non è nemmeno il doppio. Ma la dimensione dell'indice può essere significativa se non si adatta alla memoria. Quindi, quando cambio innodb_buffer_pool_size
da 1G
a 50M
ottengo i seguenti numeri:
- TINYINT:~ 960 msec
- TIMESTAMP:~ 1500 msec
Aggiorna
Per rispondere più direttamente alla domanda ho apportato alcune modifiche ai dati:
- Invece di TIMESTAMP utilizzo DATETIME
- Dato che di solito le voci vengono eliminate raramente, uso
rand(1)<0.99
(1% eliminato) invece dirand(1)<0.5
(50% cancellato) - La dimensione della tabella è cambiata da 10 milioni a 1 milione di righe.
SELECT COUNT(*)
cambiato inSELECT *
Dimensione dell'indice:
index_name | stat_value*@@innodb_page_size
------------------------------------------
PRIMARY | 25739264
deleted_at | 12075008
is_active | 11026432
Dal 99% di deleted_at
i valori sono NULL non vi è alcuna differenza significativa nella dimensione dell'indice, sebbene un DATETIME non vuoto richieda 8 byte (MariaDB).
SELECT * FROM test WHERE is_active = 1; -- 782 msec
SELECT * FROM test WHERE deleted_at is null; -- 829 msec
Eliminando entrambi gli indici, entrambe le query vengono eseguite in circa 350 msec. E rilasciando is_active
colonna il deleted_at is null
la query viene eseguita in 280 msec.
Si noti che questo non è ancora uno scenario realistico. È improbabile che tu voglia selezionare 990.000 righe su 1 milione e consegnarlo all'utente. Probabilmente avrai anche più colonne (forse incluso il testo) nella tabella. Ma mostra che probabilmente non hai bisogno di is_active
colonna (se non aggiunge informazioni aggiuntive) e che qualsiasi indice è nel migliore dei casi inutile per selezionare voci non eliminate.
Tuttavia un indice può essere utile per selezionare le righe eliminate:
SELECT * FROM test WHERE is_active = 0;
Viene eseguito in 10 msec con indice e in 170 msec senza indice.
SELECT * FROM test WHERE deleted_at is not null;
Viene eseguito in 11 msec con indice e in 167 msec senza indice.
Eliminare il is_active
colonna viene eseguita in 4 msec con indice e in 150 msec senza indice.
Quindi, se questo scenario si adatta in qualche modo ai tuoi dati, la conclusione sarebbe:elimina is_active
colonna e non creare un indice su deleted_at
colonna se si selezionano raramente voci eliminate. Oppure adatta il benchmark alle tue esigenze e trai le tue conclusioni.