Voici un benchmark MariaDB (10.0.19) avec 10M de lignes (utilisant le plugin de séquence ):
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;
Pour mesurer le temps j'utilise set profiling=1
et exécutez show profile
après avoir exécuté une requête. Du résultat du profilage, je prends la valeur de Sending data
puisque tout le reste est inférieur à une msec.
TINYINT indice :
SELECT COUNT(*) FROM test WHERE is_active = 1;
Autonomie :~ 738 ms
Horodatage indice :
SELECT COUNT(*) FROM test WHERE deleted_at is null;
Autonomie :~ 748 ms
Taille de l'index :
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'
Résultat :
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
Notez que si TIMESTAMP (4 octets) est 4 fois plus long que TYNYINT (1 octet), la taille de l'index n'est même pas deux fois plus grande. Mais la taille de l'index peut être importante si elle ne rentre pas dans la mémoire. Donc, quand je change innodb_buffer_pool_size
à partir de 1G
à 50M
j'obtiens les nombres suivants :
- TINYINT :~ 960 ms
- Horodatage :~ 1 500 ms
Mettre à jour
Pour répondre plus directement à la question, j'ai apporté quelques modifications aux données :
- Au lieu de TIMESTAMP, j'utilise DATETIME
- Étant donné que les entrées sont généralement rarement supprimées, j'utilise
rand(1)<0.99
(1 % supprimé) au lieu derand(1)<0.5
(50 % supprimé) - La taille du tableau est passée de 10 millions à 1 million de lignes.
SELECT COUNT(*)
remplacé parSELECT *
Taille de l'index :
index_name | stat_value*@@innodb_page_size
------------------------------------------
PRIMARY | 25739264
deleted_at | 12075008
is_active | 11026432
Depuis 99 % des deleted_at
les valeurs sont NULL, il n'y a pas de différence significative dans la taille de l'index, bien qu'un DATETIME non vide nécessite 8 octets (MariaDB).
SELECT * FROM test WHERE is_active = 1; -- 782 msec
SELECT * FROM test WHERE deleted_at is null; -- 829 msec
En supprimant les deux index, les deux requêtes s'exécutent en 350 ms environ. Et en supprimant le is_active
la colonne deleted_at is null
la requête s'exécute en 280 ms.
Notez que ce n'est toujours pas un scénario réaliste. Il est peu probable que vous souhaitiez sélectionner 990 000 lignes sur 1 M et les livrer à l'utilisateur. Vous aurez probablement aussi plus de colonnes (y compris peut-être du texte) dans le tableau. Mais cela montre que vous n'avez probablement pas besoin du is_active
colonne (si elle n'ajoute pas d'informations supplémentaires), et que tout index est dans le meilleur des cas inutile pour sélectionner des entrées non supprimées.
Cependant un index peut être utile pour sélectionner les lignes supprimées :
SELECT * FROM test WHERE is_active = 0;
S'exécute en 10 msec avec index et en 170 msec sans index.
SELECT * FROM test WHERE deleted_at is not null;
S'exécute en 11 msec avec index et en 167 msec sans index.
Suppression de is_active
colonne, il s'exécute en 4 msec avec index et en 150 msec sans index.
Donc, si ce scénario correspond d'une manière ou d'une autre à vos données, la conclusion serait :supprimez le is_active
colonne et ne créez pas d'index sur deleted_at
si vous sélectionnez rarement des entrées supprimées. Ou ajustez le benchmark à vos besoins et tirez votre propre conclusion.