Mysql
 sql >> Base de données >  >> RDS >> Mysql

Performance de la requête sur la colonne booléenne indexée par rapport à la colonne Datetime

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 de rand(1)<0.5 (50 % supprimé)
  • La taille du tableau est passée de 10 millions à 1 million de lignes.
  • SELECT COUNT(*) remplacé par SELECT *

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.