J'ai une base de données avec 40 millions d'entrées et je souhaite exécuter des requêtes avec la clause WHERE
suivante
...
WHERE
`POP1` IS NOT NULL
&& `VT`='ABC'
&& (`SOURCE`='HOME')
&& (`alt` RLIKE '^[AaCcGgTt]$')
&& (`ref` RLIKE '^[AaCcGgTt]$')
&& (`AA` RLIKE '^[AaCcGgTt]$')
&& (`ref` = `AA` || `alt` = `AA`)
LIMIT 10 ;
POP1
est une colonne flottante qui peut également être NULL. POP1 IS NOT NULL
devrait exclure environ 50% des entrées, c'est pourquoi je l'ai mis au début. Tous les autres termes ne réduisent le nombre que marginalement.
Entre autres, j'ai conçu un index pop1_vt_source
, qui semble ne pas être utilisé, tandis qu'un index avec vt
comme première colonne est utilisé. Sortie EXPLAIN:
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
| 1 | SIMPLE | myTab | ref | vt_source_pop1_pop2,pop1_vt_source,... | vt_source_pop1_pop2 | 206 | const,const | 20040021 | Using where |
Pourquoi l'index avec pop1
comme première colonne non utilisée? À cause de NOT
ou à cause de NULL
en général. Comment puis-je améliorer la conception de mes indices et des clauses WHERE? Même en limitant à 10 entrées, la requête prend plus de 30 secondes, bien que les 100 premières entrées du tableau doivent contenir les 10 correspondances.
C'est le NOT NULL
:
CREATE TEMPORARY TABLE `myTab` (`notnul` FLOAT, `nul` FLOAT);
INSERT INTO `myTab` VALUES (1, NULL), (1, 2), (1, NULL), (1, 2), (1, NULL), (1, 2), (1, NULL), (1, 2), (1, NULL), (1, 2), (1, NULL), (1, 2);
SELECT * FROM `myTab`;
donne:
+--------+------+
| notnul | nul |
+--------+------+
| 1 | NULL |
| 1 | 2 |
| 1 | NULL |
| 1 | 2 |
| 1 | NULL |
| 1 | 2 |
| 1 | NULL |
| 1 | 2 |
| 1 | NULL |
| 1 | 2 |
| 1 | NULL |
| 1 | 2 |
+--------+------+
Créez l'index:
CREATE INDEX `notnul_nul` ON `myTab` (`notnul`, `nul`);
CREATE INDEX `nul_notnul` ON `myTab` (`nul`, `notnul`);
SHOW INDEX FROM `myTab`;
donne:
+-------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| myTab | 1 | notnul_nul | 1 | notnul | A | 12 | NULL | NULL | YES | BTREE | | |
| myTab | 1 | notnul_nul | 2 | nul | A | 12 | NULL | NULL | YES | BTREE | | |
| myTab | 1 | nul_notnul | 1 | nul | A | 12 | NULL | NULL | YES | BTREE | | |
| myTab | 1 | nul_notnul | 2 | notnul | A | 12 | NULL | NULL | YES | BTREE | | |
+-------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
expliquer maintenant les sélections. Il semble que MySQL utilise l'index, même si vous utilisez NOT NULL
:
EXPLAIN SELECT * FROM `myTab` WHERE `notnul` IS NOT NULL;
+----+-------------+-------+-------+---------------+------------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+------------+---------+------+------+--------------------------+
| 1 | SIMPLE | myTab | index | notnul_nul | notnul_nul | 10 | NULL | 12 | Using where; Using index |
+----+-------------+-------+-------+---------------+------------+---------+------+------+--------------------------+
EXPLAIN SELECT * FROM `myTab` WHERE `nul` IS NOT NULL;
+----+-------------+-------+-------+---------------+------------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+------------+---------+------+------+--------------------------+
| 1 | SIMPLE | myTab | range | nul_notnul | nul_notnul | 5 | NULL | 6 | Using where; Using index |
+----+-------------+-------+-------+---------------+------------+---------+------+------+--------------------------+
Mais, en comparant NOT NULL
et NULL
, il semble que MySQL préfère les autres index lors de l'utilisation de NOT NULL
. Bien que cela n'ajoute évidemment aucune information. En effet, MySQL interprète NOT NULL
comme une plage comme vous pouvez le voir dans la colonne type. Je ne suis pas sûr S'il y a une solution:
EXPLAIN SELECT * FROM `myTab` WHERE `nul` IS NULL && notnul=2;
+----+-------------+-------+------+-----------------------+------------+---------+-------------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+-----------------------+------------+---------+-------------+------+--------------------------+
| 1 | SIMPLE | myTab | ref | notnul_nul,nul_notnul | notnul_nul | 10 | const,const | 1 | Using where; Using index |
+----+-------------+-------+------+-----------------------+------------+---------+-------------+------+--------------------------+
EXPLAIN SELECT * FROM `myTab` WHERE `nul` IS NOT NULL && notnul=2;
+----+-------------+-------+-------+-----------------------+------------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+-----------------------+------------+---------+------+------+--------------------------+
| 1 | SIMPLE | myTab | range | notnul_nul,nul_notnul | notnul_nul | 10 | NULL | 1 | Using where; Using index |
+----+-------------+-------+-------+-----------------------+------------+---------+------+------+--------------------------+
Je pense qu'il pourrait y avoir une meilleure implémentation dans MySQL, car NULL
est une valeur spéciale. La plupart des gens sont probablement intéressés par NOT NULL
valeurs.
Le problème n'est pas les valeurs NULL. C'est la sélectivité de l'indice. Dans votre exemple, la sélectivité de source, pop1
est mieux que la sélectivité de seulement pop1
. Il couvre plusieurs des conditions de la clause where
, il est donc plus probable de réduire le nombre de visites de page.
Vous pouvez penser que réduire le nombre de lignes de 50% est suffisant, mais ce n'est vraiment pas le cas. L'avantage des index dans une clause where
est de réduire le nombre de pages lues. Si une page a, en moyenne, au moins un enregistrement avec une valeur non NULL, alors il n'y a aucun gain à utiliser l'index. Et, s'il y a 10 enregistrements par page, alors presque chaque page aura l'un de ces enregistrements.
Vous pouvez essayer un index sur (pop1, vt, source)
. L'optimiseur devrait prendre celui-là.
En fin de compte, cependant, si la clause where
conserve des enregistrements perdus - il n'y a pas de règle mais disons 20% - alors l'index n'aidera probablement pas. Une exception serait lorsque l'index contient tous les colonnes nécessaires à la requête. Ensuite, il peut satisfaire la requête sans faire apparaître la page de données de chaque enregistrement.
Et, si un index est utilisé et que la sélectivité est élevée, les performances avec l'index peuvent être pires que les performances sans lui.