2010-07-30 4 views
3

La table compte 1 500 000 enregistrements, dont 1 250 000 ont un champ = 'z'.
J'ai besoin de sélectionner un champ aléatoire non 'z'.Pourquoi l'index a-t-il rendu cette requête plus lente?

$random = mt_rand(1, 250000); 
$query = "SELECT field FROM table WHERE field != 'z' LIMIT $random, 1"; 

Cela fonctionne bien.

Ensuite, j'ai décidé de l'optimiser et indexé field dans le tableau. Le résultat était étrange - c'était plus lent ~ 3 fois. Je l'ai testé.
Pourquoi est-il plus lent? Est-ce qu'une telle indexation ne devrait pas le rendre plus rapide?

mon ISAM

explain with index: 
id select_type table type possible_keys key key_len ref rows  Extra 
1 SIMPLE  table range field   field 758  NULL 1139287 Using 

explain without index: 
id select_type table type possible_keys key key_len ref rows  Extra 
1 SIMPLE  table ALL NULL   NULL NULL  NULL 1484672 Using where 
+1

De quel moteur s'agit-il? Montrer la sortie de EXPLAIN – Mchl

Répondre

12

Résumé

Le problème est que field est pas un bon candidat pour l'indexation, en raison de la nature de b-trees.

Explication

Supposons que vous avez une table qui a les résultats de 500.000 lancers de pièces, où le tirage au sort est soit 1 (têtes) ou 0 (queues):

CREATE TABLE toss (
    id int NOT NULL AUTO_INCREMENT, 
    result int NOT NULL DEFAULT '0', 
    PRIMARY KEY (id) 
) 

select result, count(*) from toss group by result order by result; 
+--------+----------+ 
| result | count(*) | 
+--------+----------+ 
|  0 | 250290 | 
|  1 | 249710 | 
+--------+----------+ 
2 rows in set (0.40 sec) 

Si vous Vous voulez sélectionner un tirage au sort (au hasard) où le tirage au sort était pile, alors vous devez chercher dans votre table, en choisissant un lieu de départ aléatoire.

select * from toss where result != 1 limit 123456, 1; 
+--------+--------+ 
| id  | result | 
+--------+--------+ 
| 246700 |  0 | 
+--------+--------+ 
1 row in set (0.06 sec) 

explain select * from toss where result != 1 limit 123456, 1; 
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+ 
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra  | 
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+ 
| 1 | SIMPLE  | toss | ALL | NULL   | NULL | NULL | NULL | 500000 | Using where | 
+----+-------------+-------+------+---------------+------+---------+------+--------+-------------+ 

Vous voyez que vous effectuez une recherche séquentielle dans toutes les lignes pour trouver une correspondance.

Si vous créez un index sur le champ toss, votre index contiendra deux valeurs, chacune contenant environ 250 000 entrées.

create index foo on toss (result); 
Query OK, 500000 rows affected (2.48 sec) 
Records: 500000 Duplicates: 0 Warnings: 0 

select * from toss where result != 1 limit 123456, 1; 
+--------+--------+ 
| id  | result | 
+--------+--------+ 
| 246700 |  0 | 
+--------+--------+ 
1 row in set (0.25 sec) 

explain select * from toss where result != 1 limit 123456, 1; 
+----+-------------+-------+-------+---------------+------+---------+------+--------+-------------+ 
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra  | 
+----+-------------+-------+-------+---------------+------+---------+------+--------+-------------+ 
| 1 | SIMPLE  | toss | range | foo   | foo | 4  | NULL | 154565 | Using where | 
+----+-------------+-------+-------+---------------+------+---------+------+--------+-------------+ 

Maintenant, vous recherchez moins d'enregistrements, mais le temps de recherche a augmenté de 0,06 à 0,25 secondes. Pourquoi? Parce que l'analyse séquentielle d'un index est en fait moins efficace que l'analyse séquentielle d'une table, pour les index avec un grand nombre de lignes pour une clé donnée.

Regardons les indices sur ce tableau:

show index from toss; 
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | 
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
| toss |   0 | PRIMARY |   1 | id   | A   |  500000 |  NULL | NULL |  | BTREE  |   | 
| toss |   1 | foo  |   1 | result  | A   |   2 |  NULL | NULL |  | BTREE  |   | 
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 

L'indice primaire est un bon indice: il y a 500.000 lignes, et il y a 500.000 valeurs. Disposé dans un BTREE, vous pouvez rapidement identifier une seule ligne en fonction de l'identifiant.

L'index foo est un mauvais index: il y a 500 000 lignes, mais seulement 2 valeurs possibles. C'est à peu près le pire des cas pour un BTREE - tous les frais généraux de la recherche de l'index, et encore à chercher dans les résultats.

+0

+1 pour une explication détaillée avec un exemple! – Incognito

+0

Nous ne devrions donc utiliser les indices que si les valeurs ont une grande variété (comme la clé primaire - une pour chaque ligne). Que suggérez-vous que OP devrait faire dans son cas? Il suffit de déposer l'index, fin? – Blauhirn

+0

@Blauhirn si l'index est blessant, ne pas aider, alors oui, laisser tomber l'index. Au-delà de cela, vous pourriez vouloir restructurer (dénormaliser) les données pour faciliter la sélection des données intéressantes, mais cela dépend vraiment de l'information détaillée qui n'est pas présente dans la question. –

0

En l'absence d'une clause order by, qui commence à un endroit non défini.

Et selon votre explication, l'indice n'est même pas utilisé.

+0

donc si j'ajoute 'ORDER BY id', sera-t-il utilisé? – Qiao

Questions connexes