MySQL Indexing VarChar

10

J'essaie d'indexer ma blogentriesbase de données pour de meilleures performances mais j'ai trouvé un problème.

Voici la structure:

CREATE TABLE IF NOT EXISTS `blogentries` (
  `id_id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `title_id` varchar(100) COLLATE latin1_german2_ci NOT NULL,
  `entry_id` varchar(5000) COLLATE latin1_german2_ci NOT NULL,
  `date_id` int(11) NOT NULL,
  PRIMARY KEY (`id_id`)
)
ENGINE=MyISAM
DEFAULT CHARSET=latin1
COLLATE=latin1_german2_ci
AUTO_INCREMENT=271;

Une requête comme la suivante utilise correctement l'index:

EXPLAIN SELECT id_id,title_id FROM blogentries ORDER by id_id DESC
+ ---- + ------------- + ------------- + ------- + -------- ------- + --------- + --------- + ------ + ------ + -------- ----- +
| id | select_type | table | type | touches_ possibles | clé | key_len | ref | lignes | Extra |
+ ---- + ------------- + ------------- + ------- + -------- ------- + --------- + --------- + ------ + ------ + -------- ----- +
| 1 | SIMPLE | blogentries | index | NULL | PRIMAIRE | 114 | NULL | 126 | Utilisation de l'index |
+ ---- + ------------- + ------------- + ------- + -------- ------- + --------- + --------- + ------ + ------ + -------- ----- +

Cependant, lorsque j'ajoute le entry_iddans la SELECTrequête, il utilise le filesort

EXPLAIN SELECT id_id,title_id,entry_id FROM blogentries ORDER by id_id DESC
+ ---- + ------------- + ------------- + ------ + --------- ------ + ------ + --------- + ------ + ------ + ------------ ---- +
| id | select_type | table | type | touches_ possibles | clé | key_len | ref | lignes | Extra |
+ ---- + ------------- + ------------- + ------ + --------- ------ + ------ + --------- + ------ + ------ + ------------ ---- +
| 1 | SIMPLE | blogentries | TOUS | NULL | NULL | NULL | NULL | 126 | Utilisation de filesort |
+ ---- + ------------- + ------------- + ------ + --------- ------ + ------ + --------- + ------ + ------ + ------------ ---- +

Je me demandais pourquoi cela se produit et comment je peux l'éviter? Est-ce dû à la VarChar, et cela devrait être changé pour autre chose?

J'essaie que toutes mes requêtes utilisent l'index alors que je rencontre des valeurs élevées Handler_read_rndet Handler_read_rnd_next.

Si vous avez besoin d'autres informations, je peux également les publier.


la source
filesort signifie qu'il effectue le tri sur le disque.
Kermit
Essayez d'ajouter WHERE 1=1à votre deuxième requête.
Kermit
De quelle version de MySQL s'agit-il? Quelle est votre taille de tampon de tri ( SELECT @@sort_buffer_size)?
@njk filesort est le résultat de la partie 'ORDER BY' de la requête
1
@TashPemhiwa Pas nécessairement, voir la première déclaration.
Kermit

Réponses:

6

Comme vous n'avez pas de WHEREclause dans l'une ou l'autre requête, vous retournez toutes les lignes dans les deux cas, donc je pense que l'utilisation ou la non-utilisation de l'index aurait très peu d'impact sur les performances dans ces exemples.

Joe Stefanelli
la source
Certes, MySQL devrait utiliser l'index pour le ORDER BY?
eggyal
@eggyal Pas si c'est trop grand pour la mémoire.
Kermit
@njk: Cela n'a pas de sens ... il peut parcourir l'index, dans l'ordre, sans avoir à charger le tout en mémoire. Les résultats seraient triés sans avoir à effectuer de tri de fichiers.
eggyal
@eggyal, je remets en question la taille de varchar(5000).
Kermit du
@njk: Mais cette colonne n'est ni dans l'index ni utilisée dans le tri.
eggyal
2

Comme indiqué sous ORDER BYOptimisation :

Pour les requêtes lentes pour lesquelles filesortn'est pas utilisé, essayez de réduire max_length_for_sort_dataà une valeur appropriée pour déclencher un filesort.

Dans son article de blog Qu'est-ce que read_rnd_buffer_size exactement , Peter Zaitsev explique:

Pour moi, cela signifie que depuis MySQL 4.1, cette option est utilisée dans une gamme étroite de cas - si vous récupérez quelques champs (moins que max_length_for_sort_data ), les données doivent être stockées dans le tampon de tri et le fichier de tri afin qu'il n'y ait pas besoin de read_rnd_buffer, si les colonnes sélectionnées sont longues donc elles sont plus longues que max_length_for_sort_data cela voudrait souvent dire qu'il y a des colonnes TEXT / BLOB entre elles. Il serait cependant utilisé s'il y a un grand nombre de colonnes ou si de longues colonnes VARCHAR sont utilisées - il suffit de quelques UTF8 VARCHAR (255) pour créer une ligne plus longue que max_length_for_sort_data dans sa présentation statique.

Cela suggère qu'il max_length_for_sort_datay a une limite à la taille totale des colonnes que l'on sélectionne, au-dessus de laquelle un filesortsera utilisé au lieu d'un tri basé sur un index.

Dans votre cas, la sélection entry_id(5002 octets) prend la taille totale sur la valeur par défaut de 1 Ko de cette variable et filesortest donc utilisée. Pour augmenter la limite à 8 Ko, vous pouvez faire:

SET SESSION max_length_for_sort_data = 8192;
eggyal
la source
J'ai une table avec une configuration très similaire à celle-ci, et ce paramètre ne semble pas déclencher de changement dans l'utilisation de filesort.
@muffinista: C'est intéressant. Je suppose que cela pourrait être lié à certains des autres paramètres de tampon, selon la réponse de @ RolandoMySQLDBA ?
eggyal
2

Vous avez obtenu beaucoup de réponses intéressantes ici, mais personne n'a exactement répondu à la question - pourquoi cela se produit-il? Si je comprends bien, lorsqu'une requête SELECT contient des données de longueur variable dans MySQL, et qu'il n'y a pas d'index qui correspond à TOUTES les colonnes demandées, elle utilisera toujours un tri de fichiers. La taille des données n'est pas vraiment pertinente ici. Il est difficile de trouver une réponse directe à cette question dans la documentation MySQL, mais voici un bon article de blog où quelqu'un rencontre un problème très similaire au vôtre.

Voir aussi: 10 conseils pour optimiser les requêtes MySQL (qui ne craignent pas) .

Donc, s'il est viable d'avoir un index sur entry_id, alors vous pouvez l'ajouter et être prêt. Mais je doute que ce soit une option, alors que faire?

Si vous devez faire quelque chose à ce sujet est une question distincte. Il est important de savoir que «filesort» est mal nommé dans MySQL - c'est vraiment juste le nom de l'algorithme utilisé pour trier cette requête particulière, et dans de nombreux cas, le tri se fera en fait en mémoire. Si vous ne vous attendez pas à ce que ce tableau se développe beaucoup, ce n'est probablement pas un gros problème.

D'un autre côté, si ce tableau contient un million de lignes, vous pourriez avoir un problème. Si vous devez prendre en charge la pagination des requêtes sur ce tableau, vous pourriez avoir un problème de performances très sérieux ici. Dans ce cas, partitionner vos données de longueur variable dans une nouvelle table et faire un JOIN pour les récupérer est une optimisation valide à considérer.

Voici quelques autres réponses sur SO qui parlent de cette question:

Communauté
la source
La première requête de l'OP " contient des données de longueur variable dans MySQL, et il n'y a pas d'index qui correspond à TOUTES les colonnes demandées ", mais filesortn'a apparemment pas été utilisé dans ce cas. Je pense également que même le tri d'une petite table en mémoire seule pourrait s'avérer être un impact sur les performances inacceptable: par exemple si la requête est effectuée beaucoup (et la table change de sorte que les caches ne peuvent pas être utilisés).
eggyal
Je n'ai pas le temps de le tester, mais je me demande si cela est déclenché par un VARCHAR qui nécessite 2 octets pour stocker la longueur spécifiée dans dev.mysql.com/doc/refman/5.1/en/char. html - donc la première requête s'inscrit dans cette limite mais pas la seconde.
0

Essayez d'ajouter une WHEREclause dans vos requêtes.

L'index peut être utilisé même si ORDER BY ne correspond pas exactement à l'index, tant que toutes les parties inutilisées de l'index et toutes les colonnes ORDER BY supplémentaires sont des constantes dans la clause WHERE . Dans certains cas, MySQL ne peut pas utiliser d'index pour résoudre ORDER BY , bien qu'il utilise toujours des index pour trouver les lignes qui correspondent à la clause WHERE .

http://dev.mysql.com/doc/refman/5.0/en/order-by-optimization.html


la source
Mais dans ce cas , le ORDER BY ne fait correspondre exactement à l'index, donc il n'y a pas besoin d'avoir une WHEREclause.
eggyal
J'ai une clause "où" dans la requête réelle sur le site, donc je sais que ce n'est pas la cause du tri des fichiers. Je me demande si son utilisation de varchar?
0

Dans la mesure de mes connaissances, varchar ne peut contenir qu'un maximum de 8 000 octets, soit environ 4 000 caractères. Ainsi, 5000 semblerait dépasser la limite de stockage, et dans ce cas probablement la raison pour laquelle le tri est foiré.

"varchar [(n | max)] Données de caractères non Unicode de longueur variable. n peut être une valeur comprise entre 1 et 8 000. max indique que la taille de stockage maximale est de 2 ^ 31-1 octets. La taille de stockage est la valeur réelle longueur des données saisies + 2 octets. Les données saisies peuvent avoir une longueur de 0 caractère. Les synonymes SQL-2003 pour varchar varient en fonction du caractère ou du caractère. "

J'espère que ça répond à ta question


la source
Comme indiqué sous Les types CHARetVARCHAR : "Les valeurs dans les colonnes VARCHAR sont des chaînes de longueur variable. La longueur peut être spécifiée comme une valeur de 0 à 255 avant MySQL 5.0.3 et de 0 à 65 535 dans 5.0.3 et versions ultérieures. la longueur maximale d'un VARCHARdans MySQL 5.0.3 et versions ultérieures est soumise à la taille de ligne maximale (65 535 octets, qui est partagée entre toutes les colonnes) et au jeu de caractères utilisé. "
eggyal
0

Vous n'avez que 126 lignes dans votre tableau. Même si chaque ligne est dimensionnée au maximum d'environ 5 Ko, cela signifierait que la taille totale à lire sur le disque n'est que d'environ 600 Ko - ce n'est pas beaucoup. Pour être franc, il s'agit d'une très petite quantité, probablement inférieure à la taille du cache de la plupart des lecteurs de disque modernes.

Maintenant, si le serveur doit récupérer vos données pour répondre à votre requête, l'opération la plus coûteuse consiste à les lire à partir du disque. Mais, le lire selon l'ordre des index n'est PAS toujours le moyen le plus rapide de le faire, surtout lorsque la quantité de données est si petite.

Dans votre cas, il est BEAUCOUP plus efficace de lire des données de table entières du disque en tant que bloc unique dans la mémoire (probablement en une seule opération de lecture ou de recherche de disque), puis de les trier dans la RAM pour satisfaire ORDER BY, qui est instantané par rapport au disque lire l'opération. Si le serveur lit vos données en fonction de l'index, il devra émettre jusqu'à 126 (oups!) Opérations de lecture, en cherchant plusieurs fois dans le même fichier de données.

En d'autres termes, le scan séquentiel n'est PAS toujours une mauvaise chose, et mysql n'est pas nécessairement stupide. Si vous essayez de forcer mysql à utiliser cet index, il fonctionnera probablement plus lentement que le scan séquentiel que vous avez actuellement.

Et la raison pour laquelle il utilisait l'index lorsque le champ 5 Ko n'était pas inclus est parce que les données récupérées ne constituaient pas 99% des données de la table. Lorsque vous avez inclus votre champ de 5 Ko, la requête doit désormais lire 99% des données, et il est moins coûteux de lire le tout et de le trier en mémoire par la suite.

mvp
la source
Il semble que vous embrouillez un certain nombre de choses dans Comment éviter les analyses de table complètes , qui sont liées à l'utilisation d'index pour satisfaire des JOINconditions et des WHEREclauses, et non des ORDER BYclauses.
eggyal
Exactement le contraire. Dans ce cas particulier, l'analyse complète de la table est une BONNE chose simplement parce qu'elle est PLUS RAPIDE que la lecture par ordre d'index.
0

Quelle version de MySQL utilisez-vous?

EN 5.1, j'ai essayé de configurer votre scénario et j'ai rempli quelques données factices. En utilisant les SQL que vous avez fournis, je n'obtiens qu'une analyse de table à chaque fois selon EXPLAIN. Par défaut lorsque vous utilisez order by MYSQL recourt au tri de fichiers même si l'index primaire est utilisé dans l'ordre by.


la source