2009-04-07 4 views
18

Je rencontre des problèmes de performances assez importants en raison de l'utilisation des états "ORDER BY" dans mon code SQL.MySQL n'utilise pas d'index ("Using filesort") lors de l'utilisation ORDER BY

Tout va bien tant que je n'utilise pas les instructions ORDER BY dans le SQL. Cependant, une fois que j'introduis ORDER BY: s dans le code SQL, tout ralentit considérablement en raison de l'absence d'indexation correcte. On pourrait supposer que cela serait trivial, mais à en juger par les discussions du forum, etc., cela semble être une question plutôt courante que je n'ai pas encore trouvé une réponse définitive et concise à cette question.

Question: Compte tenu du tableau ci-dessous ...

 
CREATE TABLE values_table (
    id int(11) NOT NULL auto_increment, 
    ... 
    value1 int(10) unsigned NOT NULL default '0', 
    value2 int(11) NOT NULL default '0', 
    PRIMARY KEY (id), 
    KEY value1 (value1), 
    KEY value2 (value2), 
) ENGINE=MyISAM AUTO_INCREMENT=2364641 DEFAULT CHARSET=utf8; 

... comment puis-je créer des index qui seront utilisés lors de l'interrogation de la table pour un valeur1 -range lors du tri sur la valeur de value2?

Actuellement, l'extraction est OK lorsque vous n'utilisez PAS la clause ORDER BY.

Voir les suivantes EXPLAIN QUERY:

 
OK, when NOT using ORDER BY: 

EXPLAIN select ... from values_table this_ where this_.value1 between 12345678 and 12349999 limit 10; 

+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref | rows | Extra  | 
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+ 
| 1 | SIMPLE  | this_ | range | value1  | value1 | 4  | NULL | 3303 | Using where | 
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+ 
 
However, when using ORDER BY I get "Using filesort": 

EXPLAIN select ... from values_table this_ where this_.value1 between 12345678 and 12349999 order by this_.value2 asc limit 10; 

+----+-------------+-------+-------+---------------+----------+---------+------+------+-----------------------------+ 
| id | select_type | table | type | possible_keys | key  | key_len | ref | rows | Extra      | 
+----+-------------+-------+-------+---------------+----------+---------+------+------+-----------------------------+ 
| 1 | SIMPLE  | this_ | range | value1  | value1 | 4  | NULL | 3303 | Using where; Using filesort | 
+----+-------------+-------+-------+---------------+----------+---------+------+------+-----------------------------+ 

Quelques informations supplémentaires sur le contenu de la table:

 
SELECT MIN(value1), MAX(value1) FROM values_table; 
+---------------+---------------+ 
| MIN(value1) | MAX(value2) | 
+---------------+---------------+ 
|    0 | 4294967295 | 
+---------------+---------------+ 

... 

SELECT MIN(value2), MAX(value2) FROM values_table; 
+---------------+---------------+ 
| MIN(value2) | MAX(value2) | 
+---------------+---------------+ 
|    1 |  953359 | 
+---------------+---------------+ 

S'il vous plaît laissez-moi savoir si d'autres informations sont nécessaires pour répondre à la question .

Merci beaucoup d'avance!

Mise à jour # 1: Ajout d'un nouvel indice composite (ALTER TABLE values_table ADD INDEX (valeur1, valeur2),) ne résout pas le problème. Vous aurez toujours "Using filesort" après avoir ajouté un tel index. Une contrainte que je n'ai pas mentionnée dans ma question est que je préfère changer la structure de la table (par exemple ajouter des index, etc.) plutôt que de changer les requêtes SQL utilisées. Les requêtes SQL sont générées automatiquement à l'aide d'Hibernate, donc considérez celles plus ou moins corrigées.

+0

Je suppose que vous vouliez dire valeur1, valeur2 dans votre mise à jour, oui? – paxdiablo

+0

Peu importe, cela ne fonctionnerait pas de toute façon à cause de l'explication de @ Quassnoi sur la gamme value1. Cela aurait fonctionné pour une seule valeur de valeur1 mais je n'ai pas assez bien lu la question. Bonne chance. – paxdiablo

+0

Utilisez-vous les champs directement dans votre requête ou utilisez-vous des fonctions? Comme un champ d'horodatage et SEMAINE (horodatage). –

Répondre

19

Vous ne pouvez pas utiliser d'index dans ce cas, car vous utilisez une condition de filtrage RANGE.

Si vous utilisez quelque chose comme:

SELECT * 
FROM values_table this_ 
WHERE this_.value1 = @value 
ORDER BY 
     value2 
LIMIT 10 

, puis en créant un indice composite sur (VALUE1, VALUE2) serait utilisé à la fois pour le filtrage et la commande.

Mais vous utilisez une condition à distance, c'est pourquoi vous aurez besoin d'effectuer la commande de toute façon.

Votre indice composite ressemblera à ceci:

 
value1 value2 
----- ------ 
1  10 
1  20 
1  30 
1  40 
1  50 
1  60 
2  10 
2  20 
2  30 
3  10 
3  20 
3  30 
3  40 

, et si vous sélectionnez 1 et 2 dans value1, vous ne comprends toujours pas un ensemble de value2 triés.

Si votre index sur value2 n'est pas très sélectif (.. I e il n'y a pas beaucoup DISTINCT value2 dans le tableau), vous pouvez essayer:

CREATE INDEX ix_table_value2_value1 ON mytable (value2, value1) 

/* Note the order, it's important */  

SELECT * 
FROM (
     SELECT DISTINCT value2 
     FROM mytable 
     ORDER BY 
       value2 
     ) q, 
     mytable m 
WHERE m.value2 >= q.value2 
     AND m.value2 <= q.value2 
     AND m.value1 BETWEEN 13123123 AND 123123123 

Ceci est appelé une méthode d'accès SKIP SCAN. MySQL ne le supporte pas directement, mais il peut être émulé comme ceci. L'accès RANGE sera utilisé dans ce cas, mais vous n'aurez probablement aucun avantage de performance à moins que DISTINCT value2 ne comprenne moins de 1% lignes.

utilisation Note de:

m.value2 >= q.value2 
AND m.value2 <= q.value2 

au lieu de

m.value2 = q.value2 

Cela rend MySQL effectuer RANGE la vérification de chaque boucle.

+0

+1 pour avoir ramassé le problème de portée que j'ai raté :-) – paxdiablo

+0

Merci pour votre réponse complète. En supposant que je ne puisse pas changer les requêtes SQL utilisées (celles-ci sont générées automatiquement par Hibernate), croyez-vous que c'est impossible à résoudre (en ajoutant une meilleure indexation)? – knorv

+0

Une autre question: Si l'interrogation de gamme est le problème, comment tout semble bien quand vous n'utilisez pas ORDER BY? Désolé si j'ai manqué ce détail. – knorv

0

Il me semble que vous avez deux clés totalement indépendantes, une pour value1 et une pour value2. Par conséquent, lorsque vous utilisez la clé value1 pour récupérer, les enregistrements ne sont pas nécessairement renvoyés dans l'ordre de value2, ils doivent donc être triés. C'est toujours mieux qu'une analyse de table complète puisque vous ne faites que trier les enregistrements qui satisfont votre clause "where value1".

Je pense (si cela est possible en MySQL), une clé composite sur (value1, value2) résoudrait cela.

Essayer:

CREATE TABLE values_table (
    id int(11) NOT NULL auto_increment, 
    ... 
    value1 int(10) unsigned NOT NULL default '0', 
    value2 int(11) NOT NULL default '0', 
    PRIMARY KEY (id), 
    KEY value1 (value1), 
    KEY value1and2 (value1,value2), 
) ENGINE=MyISAM AUTO_INCREMENT=2364641 DEFAULT CHARSET=utf8; 

(ou l'équivalent ALTER TABLE), en supposant que a la syntaxe correcte dans MySQL pour une clé composite.Dans toutes les bases de données que je connais (et je dois admettre que MySQL n'en fait pas partie), le moteur DB doit sélectionner la valeur value1and2 pour récupérer les lignes et elles seront déjà triées dans value2-within- value1 order, donc pas besoin d'un tri par fichier.

Vous pouvez toujours conserver la clé value2 si vous en avez besoin.

+0

Salut, merci pour votre réponse rapide. J'ai essayé votre solution suggérée, et malheureusement cela n'a pas fonctionné. J'ai ajouté une précision à ma question. – knorv

+0

Pas de problèmes, on dirait que @Quassnoi a plus de connaissances sur MySQL, donc je vais vous laisser faire. Son explication de la raison pour laquelle le tri est requis pour les valeurs à distance1 est quelque chose que je n'ai pas compris dans la question - DB2 aurait des problèmes similaires. Remarquant comme communauté-wiki pour que personne d'autre ne fasse la même erreur. – paxdiablo

+0

Il y a une sorte de bug SO qui m'empêche de marquer la question community-wiki. – paxdiablo