Index de couverture utilisé malgré la colonne manquante

8

J'ai la requête suivante, en utilisant MariaDB 10 / InnoDB:

SELECT id, sender_id, receiver_id, thread_id, date_created, content 
FROM user_message 
WHERE thread_id = 12345 
  AND placeholder = FALSE
ORDER BY date_created DESC 
LIMIT 20

Cette requête récupère les messages selon les conditions données et les trie par date de création.

J'ai un indice de couverture (thread_id, date_created).

Lors de l'exécution d'EXPLAIN, l'index correct est utilisé et j'obtiens la sortie "Using where", bien que la requête utilise une colonne au milieu de l'instruction qui ne se trouve pas dans l'index. Je peux utiliser n'importe quelle valeur pour "placeholder = x" et le résultat est le même.

Si je modifie le tri pour utiliser une autre colonne, EXPLAIN indique correctement "Using where. Using filesort".

J'ai un moment qui me gratte la tête. Quelqu'un pourrait-il faire la lumière sur ce sujet? Ce que je m'attendrais à voir, c'est qu'un tri de fichiers supplémentaire serait nécessaire car l'index de couverture n'a pas pu être complètement utilisé en raison de la colonne supplémentaire.

À M
la source

Réponses:

8

Cas A
Requête:

WHERE thread_id = 12345 
  AND placeholder = FALSE
ORDER BY some_column DESC 
LIMIT 20

Indice:

(thread_id, date_created)

Plan:

Index is used
Using Where
Using filesort

Pas de problème là-bas, non? Si l'index est utilisé (pour correspondre partiellement à la WHEREcondition), nous avons encore besoin d'une opération de tri pour classer les résultats par some_column(qui n'est pas dans l'index). Nous avons également besoin d'une vérification supplémentaire (Utilisation de Où) pour ne conserver que les lignes qui correspondent à la 2ème condition. D'ACCORD.


Cas B (la question)
Requête:

WHERE thread_id = 12345 
  AND placeholder = FALSE
ORDER BY date_created DESC 
LIMIT 20

Indice:

(thread_id, date_created)

Plan:

Index is used
Using Where
-- no "Using filesort"

Alors, pourquoi n'a-t-il pas besoin d'un tri ici ? Parce que l'index est suffisant pour trier comme la requête le souhaite. Il y a bien sûr le problème supplémentaire de la condition supplémentaire ( AND placeholder = FALSE) qui n'est pas couverte par l'index.

OK mais nous n'avons pas vraiment besoin d'un tri ici. L'index peut nous fournir des résultats qui correspondent à la première condition ( WHERE thread_id = 12345) et sont dans l'ordre souhaité pour la sortie. La seule vérification supplémentaire dont nous avons besoin - et ce que fait le plan - est d'obtenir les lignes de la table, dans l'ordre fourni par l'index, et de vérifier cette 2ème condition jusqu'à ce que nous obtenions 20 correspondances. C'est ce que signifie ** Utiliser où "".

Nous pouvons obtenir les 20 matchs dans les 20 premières lignes (donc vraiment bons et rapides) ou dans les 100 premiers (toujours probablement assez rapides) ou dans les premiers 1000000 (probablement très, très lents) ou nous pouvons obtenir seulement 19 matches de la même après avoir lu toutes les lignes correspondantes de l'index (vraiment très lent sur une grande table). Tout dépend de la distribution des données.


Cas C (plan encore meilleur)
Requête:

WHERE thread_id = 12345 
  AND placeholder = FALSE
ORDER BY date_created DESC 
LIMIT 20

Indice:

(placeholder, thread_id, date_created)

Plan:

Index is used
-- no "Using Where"
-- no "Using filesort"

Maintenant, notre index correspond à la fois aux conditions et à la commande par. Le plan est assez simple: obtenez les * 20 premières correspondances de l'index et lisez les lignes correspondantes du tableau. Aucune vérification supplémentaire (pas d '"utilisation où") et aucun tri (pas d' "utilisation de tri de fichiers") nécessaires.

first *: les 20 premiers lors de la lecture de l'index vers l'arrière depuis la fin (comme nous l'avons fait ORDER BY .. DESC) mais ce n'est pas un problème. Les index B-tree peuvent être lus en avant et en arrière avec des performances presque égales.

ypercubeᵀᴹ
la source
7
  • L'utilisation de l'index indique un " indice de couverture " - Toutes les colonnes n'importe où dans le SELECTsont n'importe où dans le même index. Donc, vous n'avez pas d'index "couvrant". Et il n'est pas pratique de faire un index de couverture pour votre requête (trop de colonnes mentionnées).
  • Utilisation où - principalement du bruit.
  • Utilisation de filesort - La requête a besoin d'un tri, mais elle peut être en RAM ou dans une table temporaire. Et il peut y avoir plusieurs sortes (par exemple, GROUP BY x ORDER BY b)
  • L'un ou l'autre de ceux-ci permettra de ne regarder que 20 lignes; tout autre index nécessitera de toucher plus de lignes, éventuellement la table entière:

    INDEX(thread_id, placeholder, date_created)
    INDEX(placeholder, thread_id, date_created)
  • Non, la cardinalité des composants d'un index composite n'a pas d'importance lors de la commande des colonnes dans l'index.

Mon livre de recettes explique comment dériver l'indice optimal, étant donné a SELECT.

Rick James
la source
Merci pour le livre de cuisine - très belle feuille.
Tom