J'essaie d'indexer ma blogentries
base 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_id
dans la SELECT
requê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_rnd
et Handler_read_rnd_next
.
Si vous avez besoin d'autres informations, je peux également les publier.
WHERE 1=1
à votre deuxième requête.SELECT @@sort_buffer_size
)?Réponses:
Comme vous n'avez pas de
WHERE
clause 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.la source
ORDER BY
?varchar(5000)
.Comme indiqué sous
ORDER BY
Optimisation :Dans son article de blog Qu'est-ce que read_rnd_buffer_size exactement , Peter Zaitsev explique:
Cela suggère qu'il
max_length_for_sort_data
y a une limite à la taille totale des colonnes que l'on sélectionne, au-dessus de laquelle unfilesort
sera 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 etfilesort
est donc utilisée. Pour augmenter la limite à 8 Ko, vous pouvez faire:la source
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:
la source
filesort
n'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).Essayez d'ajouter une
WHERE
clause dans vos requêtes.http://dev.mysql.com/doc/refman/5.0/en/order-by-optimization.html
la source
ORDER BY
ne fait correspondre exactement à l'index, donc il n'y a pas besoin d'avoir uneWHERE
clause.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
CHAR
etVARCHAR
: "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'unVARCHAR
dans 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é. "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.
la source
JOIN
conditions et desWHERE
clauses, et non desORDER BY
clauses.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