2011-03-09 3 views
0

Je dois trouver le meilleur indice pour cette requête:Index IN et une gamme

SELECT c.id id, type 
FROM Content c USE INDEX (type_proc_last_cat) 
LEFT JOIN Battles b ON c.id = b.id 
WHERE type = 1 
    AND processing_status = 1 
    AND category IN (13, 19) 
    AND status = 4 
ORDER BY last_change DESC 
LIMIT 100"; 

Les tableaux ressemblent à ceci:

mysql> describe Content; 
+-------------------+---------------------+------+-----+---------+-------+ 
| Field    | Type    | Null | Key | Default | Extra | 
+-------------------+---------------------+------+-----+---------+-------+ 
| id    | bigint(20) unsigned | NO | PRI | NULL |  | 
| type    | tinyint(3) unsigned | NO | MUL | NULL |  | 
| category   | bigint(20) unsigned | NO |  | NULL |  | 
| processing_status | tinyint(3) unsigned | NO |  | NULL |  | 
| last_change  | int(10) unsigned | NO |  | NULL |  | 
+-------------------+---------------------+------+-----+---------+-------+ 

mysql> show indexes from Content; 
+---------+------------+---------------------+--------------+-------------------+-----------+-------------+----------+--------+------+------------+---------+ 
| Table | Non_unique | Key_name   | Seq_in_index | Column_name  | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | 
+---------+------------+---------------------+--------------+-------------------+-----------+-------------+----------+--------+------+------------+---------+ 
| Content |   0 | PRIMARY    |   1 | id    | A   |  4115 |  NULL | NULL |  | BTREE  |   | 
| Content |   1 | type_proc_last_cat |   1 | type    | A   |   4 |  NULL | NULL |  | BTREE  |   | 
| Content |   1 | type_proc_last_cat |   2 | processing_status | A   |   20 |  NULL | NULL |  | BTREE  |   | 
| Content |   1 | type_proc_last_cat |   3 | last_change  | A   |  4115 |  NULL | NULL |  | BTREE  |   | 
| Content |   1 | type_proc_last_cat |   4 | category   | A   |  4115 |  NULL | NULL |  | BTREE  |   | 
+---------+------------+---------------------+--------------+-------------------+-----------+-------------+----------+--------+------+------------+---------+ 


mysql> describe Battles; 
+---------------------+---------------------+------+-----+---------+-------+ 
| Field    | Type    | Null | Key | Default | Extra | 
+---------------------+---------------------+------+-----+---------+-------+ 
| id     | bigint(20) unsigned | NO | PRI | NULL |  | 
| status    | tinyint(4) unsigned | NO |  | NULL |  | 
| status_last_changed | int(11) unsigned | NO |  | NULL |  | 
+---------------------+---------------------+------+-----+---------+-------+ 

mysql> show indexes from Battles; 
+---------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | 
+---------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
| Battles |   0 | PRIMARY |   1 | id   | A   |  1215 |  NULL | NULL |  | BTREE  |   | 
| Battles |   0 | id_status |   1 | id   | A   |  1215 |  NULL | NULL |  | BTREE  |   | 
| Battles |   0 | id_status |   2 | status  | A   |  1215 |  NULL | NULL |  | BTREE  |   | 
+---------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 

Et j'obtenir une sortie comme ceci:

mysql> explain 
    -> SELECT c.id id, type 
    -> FROM Content c USE INDEX (type_proc_last_cat) 
    -> LEFT JOIN Battles b USE INDEX (id_status) ON c.id = b.id 
    -> WHERE type = 1 
    ->  AND processing_status = 1 
    ->  AND category IN (13, 19) 
    ->  AND status = 4 
    -> ORDER BY last_change DESC 
    -> LIMIT 100; 
+----+-------------+-------+--------+--------------------+--------------------+---------+-----------------------+------+--------------------------+ 
| id | select_type | table | type | possible_keys  | key    | key_len | ref     | rows | Extra     | 
+----+-------------+-------+--------+--------------------+--------------------+---------+-----------------------+------+--------------------------+ 
| 1 | SIMPLE  | c  | ref | type_proc_last_cat | type_proc_last_cat | 2  | const,const   | 1352 | Using where; Using index | 
| 1 | SIMPLE  | b  | eq_ref | id_status   | id_status   | 9  | wtm_master.c.id,const | 1 | Using where; Using index | 
+----+-------------+-------+--------+--------------------+--------------------+---------+-----------------------+------+--------------------------+ 

Le problème est le nombre de lignes pour la table de contenu. Il semble que MySQL est incapable d'utiliser efficacement last_change et category dans l'index type_proc_last_cat. Si je change l'ordre de last_change et category, moins de lignes sont sélectionnées mais il en résulte un filesort pour ORDER BY, ce qui signifie qu'il extrait toutes les lignes correspondantes de la base de données. C'est pire, puisqu'il y a plus de 100 000 lignes dans les deux tableaux. Les tables sont toutes les deux InnoDB, donc gardez à l'esprit que la clé PRIMARY est ajoutée à tous les autres index. Donc, l'indice que l'index type_proc_last_cat ci-dessus se comporte aime qu'il soit activé (type, processing_status, last_change, category, id). Je suis conscient que je pourrais changer la clé PRIMAIRE pour les batailles à (id, statut) et laisser tomber l'index id_status (et je peux le faire).

Éditer: Toute valeur pour le type, la catégorie, l'état de traitement et l'état est inférieure à 20% des valeurs totales. last_change et status_last_change sont des horodatages unix.

Edit: Si j'utilise un index différent avec category et last_change dans l'ordre inverse, je reçois ceci:

mysql> show indexes from Content; 
+---------+------------+---------------------+--------------+-------------------+-----------+-------------+----------+--------+------+------------+---------+ 
| Table | Non_unique | Key_name   | Seq_in_index | Column_name  | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | 
+---------+------------+---------------------+--------------+-------------------+-----------+-------------+----------+--------+------+------------+---------+ 
| Content |   0 | PRIMARY    |   1 | id    | A   |  4115 |  NULL | NULL |  | BTREE  |   | 
| Content |   1 | type_proc_cat_last |   1 | type    | A   |   6 |  NULL | NULL |  | BTREE  |   | 
| Content |   1 | type_proc_cat_last |   2 | processing_status | A   |   26 |  NULL | NULL |  | BTREE  |   | 
| Content |   1 | type_proc_cat_last |   3 | category   | A   |   228 |  NULL | NULL |  | BTREE  |   | 
| Content |   1 | type_proc_cat_last |   4 | last_change  | A   |  4115 |  NULL | NULL |  | BTREE  |   | 
+---------+------------+---------------------+--------------+-------------------+-----------+-------------+----------+--------+------+------------+---------+ 


mysql> explain SELECT c.id id, type FROM Content c USE INDEX (type_proc_cat_last) LEFT JOIN Battles b 
USE INDEX (id_status) ON c.id = b.id WHERE type = 1  AND processing_status = 1  AND category IN (13, 19)  AND status = 4 ORDER BY last_change DESC LIMIT 100; 
+----+-------------+-------+-------+--------------------+--------------------+---------+-----------------------+------+------------------------------------------+ 
| id | select_type | table | type | possible_keys  | key    | key_len | ref     | rows | Extra         | 
+----+-------------+-------+-------+--------------------+--------------------+---------+-----------------------+------+------------------------------------------+ 
| 1 | SIMPLE  | c  | range | type_proc_cat_last | type_proc_cat_last | 10  | NULL     | 165 | Using where; Using index; Using filesort | 
| 1 | SIMPLE  | b  | ref | id_status   | id_status   | 9  | wtm_master.c.id,const | 1 | Using where; Using index     | 
+----+-------------+-------+-------+--------------------+--------------------+---------+-----------------------+------+------------------------------------------+ 

Le filesort me inquiète car il me dit MySQL tire toutes les lignes de correspondance d'abord, avant le tri. Ce sera un gros problème quand il y en aura plus de 100 000.

Répondre

0

rows Le champ EXPLAIN ne reflète pas le nombre de lignes réellement lues. Il reflète le nombre de lignes possibles. En outre, il ne dépend pas de LIMIT, car LIMIT est appliqué après que le plan a été calculé.

Vous n'avez donc pas besoin de vous en préoccuper.

Aussi je vous suggère d'échanger last_change et category dans type_proc_last_cat si MySQL peut essayer d'utiliser la dernière partie de l'indice (last_change) à des fins de tri.

+0

Alors dans ce cas, MySQL continue de tirer des lignes dans l'ordre 'last_change DESC' jusqu'à ce qu'il trouve' LIMIT 100' qui corresponde '' category IN (13, 19) '? –

+0

@Mark Rose: oui. Mysql compte le nombre de lignes attendu et arrête l'exécution. http://dev.mysql.com/doc/refman/5.1/fr/limit-optimization.html - regardez le deuxième élément ici. – zerkms

+0

J'ai essayé de changer l'ordre comme vous l'avez suggéré, mais je me retrouve avec un fichier. J'ai mis à jour la question. –