2012-06-22 1 views
0

Je suis en train d'exécuter la requête suivante dans ma base de données:indice particulier non utilisé par MySQL

SELECT * FROM ts_cards WHERE (cardstatus= 2 OR cardstatus= 3) AND (cardtype= 1 OR cardtype= 2) ORDER BY cardserial DESC LIMIT 10;

Tous les trois champs (cardstatus, cardType et cardserial) sont indexés:

mysql> SHOW INDEX FROM ts_cards; 
    +----------+------------+----------------+--------------+-------------------+-----------+-------------+----------+--------+------+------------+---------+ 
    | Table | Non_unique | Key_name  | Seq_in_index | Column_name  | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | 
    +----------+------------+----------------+--------------+-------------------+-----------+-------------+----------+--------+------+------------+---------+ 
    | ts_cards |   0 | PRIMARY  |   1 | card_id   | A   | 15000134 |  NULL | NULL |  | BTREE  |   | 
    | ts_cards |   1 | CardID   |   1 | cardserial  | A   | 15000134 |  NULL | NULL |  | BTREE  |   | 
    | ts_cards |   1 | CardType  |   1 | cardtype   | A   |   17 |  NULL | NULL |  | BTREE  |   | 
    | ts_cards |   1 | CardHolder  |   1 | cardstatusholder | A   |   17 |  NULL | NULL |  | BTREE  |   | 
    | ts_cards |   1 | CardExpiration |   1 | cardexpiredstatus | A   |   17 |  NULL | NULL |  | BTREE  |   | 
    | ts_cards |   1 | CardStatus  |   1 | cardstatus  | A   |   17 |  NULL | NULL |  | BTREE  |   | 
    +----------+------------+----------------+--------------+-------------------+-----------+-------------+----------+--------+------+------------+---------+ 
    6 rows in set (0.22 sec) 

(Oui, je sais que les noms de l'indice sucent)

Toutefois, par défaut, MySQL utilise l'index seulement cardstatus:

mysql> EXPLAIN SELECT * FROM `ts_cards` WHERE (cardstatus= 2 OR cardstatus= 3) AND (cardtype= 1 OR cardtype= 2) ORDER BY cardserial DESC LIMIT 10; 
    +----+-------------+----------+-------+---------------------+------------+---------+------+---------+-----------------------------+ 
    | id | select_type | table | type | possible_keys  | key  | key_len | ref | rows | Extra      | 
    +----+-------------+----------+-------+---------------------+------------+---------+------+---------+-----------------------------+ 
    | 1 | SIMPLE  | ts_cards | range | CardType,CardStatus | CardStatus | 1  | NULL | 3215967 | Using where; Using filesort | 
    +----+-------------+----------+-------+---------------------+------------+---------+------+---------+-----------------------------+ 
    1 row in set (0.00 sec) 

(Il ne considère même pas l'index sur cardserial mais je suppose que c'est un autre problème.)

L'utilisation « essentielle » ou « KEY FORCE » peut faire utiliser l'index de cardType, mais pas les deux cardType et cardstatus:

mysql> EXPLAIN SELECT * FROM `ts_cards` FORCE KEY (CardType) WHERE (cardstatus= 2 OR cardstatus= 3) AND (cardtype= 1 OR cardtype= 2) ORDER BY cardserial DESC LIMIT 10; 
    +----+-------------+----------+-------+---------------+----------+---------+------+---------+-----------------------------+ 
    | id | select_type | table | type | possible_keys | key  | key_len | ref | rows | Extra      | 
    +----+-------------+----------+-------+---------------+----------+---------+------+---------+-----------------------------+ 
    | 1 | SIMPLE  | ts_cards | range | CardType  | CardType | 1  | NULL | 6084861 | Using where; Using filesort | 
    +----+-------------+----------+-------+---------------+----------+---------+------+---------+-----------------------------+ 
    1 row in set (0.00 sec) 

    mysql> EXPLAIN SELECT * FROM `ts_cards` FORCE KEY (CardType,CardStatus) WHERE (cardstatus= 2 OR cardstatus= 3) AND (cardtype= 1 OR cardtype= 2) ORDER BY cardserial DESC LIMIT 10; 
    +----+-------------+----------+-------+---------------------+------------+---------+------+---------+-----------------------------+ 
    | id | select_type | table | type | possible_keys  | key  | key_len | ref | rows | Extra      | 
    +----+-------------+----------+-------+---------------------+------------+---------+------+---------+-----------------------------+ 
    | 1 | SIMPLE  | ts_cards | range | CardType,CardStatus | CardStatus | 1  | NULL | 3215967 | Using where; Using filesort | 
    +----+-------------+----------+-------+---------------------+------------+---------+------+---------+-----------------------------+ 
    1 row in set (0.00 sec) 

Comment puis-je forcer MySQL à utiliser les deux index pour accélérer la requête? Les deux types cardtype et cardstatus semblent être définis de la même façon, mais cardstatus semble avoir préséance sur cardtype.

Répondre

0

IIRC, MySQL ne peut pas utiliser deux index distincts dans la même requête. Pour utiliser les deux index, MySQL devrait les fusionner en un seul (link to manual). Voici an example si une telle fusion (cliquez sur "Voir le plan d'exécution"). Notez le "index_merge" du premier SELECT.

Désistement: Je suis pas absolument sûr de l'information ci-dessus. Dans votre cas, malgré vos allusions, l'optimiseur considère toujours que la numérisation directe de la deuxième table est plus rapide que la fusion d'index (vos tables ont probablement un très grand nombre de lignes, donc une très grande, coûteuse à ... manipuler l'index).

Je conseille:

ALTER TABLE ADD INDEX CardTypeStatus (cardtype, cardstatus); 

Cela crée un index sur les deux colonnes. Votre requête va probablement être capable d'utiliser cet index. Vous pouvez vouloir ensuite supprimer votre index CardType: les requêtes peuvent toujours utiliser l'index à deux colonnes, même si elles effectuent une recherche sur la colonne cardtype uniquement (mais pas si elles effectuent une recherche sur cardstatus uniquement).

Plus d'informations sur les index multi-colonnes: http://dev.mysql.com/doc/refman/5.5/en/multiple-column-indexes.html

Questions connexes