2011-07-12 3 views
2

Actuellement, le tableau présente les indices suivants:Aide Optimisation d'un SELECT MySQL avec ORDER BY

  1. forum_id_index
  2. other_forum_id_index
  3. forum_id_on_other_forum_id_index => [forum_id, other_forum_id]

La requête:

SELECT `topics.*` 
FROM `topics` 
WHERE (table.forum_id = ? OR table.other_forum_id = ?) 
ORDER by sticky, replied_at DESC LIMIT 25 

J'ai essayé d'ajouter des indices sur les points suivants:

  1. collant
  2. replied_at
  3. [collant, replied_at]
  4. [forum_id, other_forum_id, collant, replied_at]
  5. [collant, replied_at , forum_id, other_forum_id]

Ceci est pour un forum, en essayant d'obtenir le top 25 des sujets dans le forum, mais en plaçant sticky sujets (collant est un champ binaire pour collant/non-collant) en haut.

J'ai lu à peu près tout ce que je peux obtenir sur l'optimisation ORDER BY, mais pas de chance. C'est sur MySQL 5.1, INNODB. Toute aide serait grandement appréciée.

Permet de modifier

Comme demandé dans les commentaires (désolé si je le fais mal - nouveau pour l'affichage sur SU). Résultats de EXPLIQUER actuellement:

id = 1

select_type = SIMPLE

table = sujets

type = index_merge

possible_keys = index_topics_on_forum_id, index_topics_on_sticky_and_replied_at, index_topics_on_forum_id_and_replied_at, index_topics_on_video_forum_id, index_forum_id_on_video_forum_id ,

clés = index_topics_on_forum_id, index_topics_on_video_forum_id

key_len = 5,5

ref = NULL

lignes = 13584

supplémentaire = Utiliser l'union (index_topics_on_forum_id, index_topics_on_video_forum_id); En utilisant où; Utilisation de filesort

AFFICHER INDEX ES DE sujets retourne https://gist.github.com/1079454 - Impossible d'obtenir la mise en forme pour apparaître ici bien.

EDIT 2

SELECT `topics`.* 
FROM `topics` 
WHERE topics.forum_id=4 
ORDER BY sticky desc, replied_at DESC 

Runs incroyablement rapide (1,4 ms). Il en va de même pour la requête lorsque je change topic.forum_id en topics.video_forum_id - mais pas quand je les ai tous les deux dans la requête avec un ou.

+1

Pouvez-vous montrer la sortie pour: SHOW indexes topics'? Pouvez-vous afficher la sortie pour: 'EXPLAIN SELECT ....'? (Il suffit donc de placer le mot 'EXPLAIN' devant votre requête – Eljakim

+0

@entity Combien de lignes retourne-t-il sans LIMIT 25? – Karolis

Répondre

0

index Try [forum_id, collant, replied_at desc] et [other_forum_id, collants, replied_at desc]

Vous pouvez essayer libellé de la requête en tant que syndicat, ainsi:

SELECT `topics.*` FROM `topics` WHERE (table.forum_id = ?) 
UNION 
SELECT `topics.*` FROM `topics` WHERE (table.other_forum_id = ?) 
ORDER by sticky, replied_at DESC LIMIT 25 
+0

L'idée de deux index est bonne, mais je pense qu'ils ne fonctionneront pas pour votre requête :) Je ne pense pas qu'il soit possible de trier en utilisant index quand UNION a été appliqué. – Karolis

+0

@Karolis - Oui, j'espérais que l'optimiseur reconnaîtrait que les deux requêtes pouvaient utiliser l'index pour retourner dans le bon ordre, et ensuite faire un tri par fusion. C'est beaucoup à demander à l'optimiseur, cependant. En tout cas, avec les deux indices cela devrait être une requête très rapide à effectuer. Si la version originale ou le formulaire UNION ne fonctionne pas rapidement, vous devez trouver la bonne façon de le décomposer afin que l'optimiseur fasse le bon choix, peut-être en utilisant des tables temporaires. – antlersoft

0

Utilisez MySQL pour EXPLAIN commande pour en apprendre davantage sur les coûts associés à votre requête:

EXPLAIN SELECT ... 

Regardez pour la table-scans, qui sont susceptibles d'être coûteux. Par ailleurs, MySQL peut utiliser ou non un index. Cela dépend uniquement de la façon dont l'optimiseur de requête comprend votre requête.

FORCE INDEX peut être utile, car cette option indique à MySQL que les analyses de table seront très coûteuses. Jetez un oeil here.

1

Je pense que cela devrait être très rapide.

Index:

ALTER TABLE `topics` 
    ADD INDEX `forum` (`forum_id` ASC, `sticky` ASC, `replied_at` DESC), 
    ADD INDEX `other_forum` (`other_forum_id` ASC, `sticky` ASC, `replied_at` DESC); 

Requête:

(
    SELECT `topics.*` 
    FROM `topics` USE INDEX (`forum`) 
    WHERE `topics`.forum_id = ? 
    ORDER by sticky, replied_at DESC 
    LIMIT 25 
) UNION (
    SELECT `topics.*` 
    FROM `topics` USE INDEX (`other_forum`) 
    WHERE `topics`.other_forum_id = ? 
    ORDER by sticky, replied_at DESC 
    LIMIT 25 
) 
ORDER by sticky, replied_at DESC 
LIMIT 25 
+0

merci pour la réponse. J'essaie de voir s'il y a un moyen de le faire sans les syndicats, comme je l'appelle de Rails en ce moment. Dans le pire des cas, je peux utiliser Model.find_by_sql mais j'essaie d'éviter d'incorporer trop de sql pur dans nos modèles. – entity

+0

Hmm - J'ai couru cette requête w/ces index et il était réellement plus lent que le courant (pas de beaucoup - 300ms vs 250ms). C'est juste bizarre que sans OR (c'est-à-dire juste des sujets SELECT. * FROM sujets WHERE topics.forum_id =? ORDER BY collant, reply_at DESC) - c'est incroyablement rapide, et c'est vrai si c'est juste le video_forum_id. Mais quand il inclut la RO, c'est beaucoup plus lent. – entity

+0

@entity Je suis un peu surpris que la requête sans ** OU ** s'exécute instantanément, mais UNION de ces deux requêtes rapides - PAS. Avez-vous utilisé les mêmes index et requêtes que dans ma réponse (sans aucune modification)? Les parties 'ASC/DESC' dans les index et dans les clauses' ORDER BY' DOIVENT être identiques. Il serait très utile de voir une EXPLAIN de ma requête et une EXPLAIN de la requête rapide sans OR. Je crois que cela nous permettra de mieux comprendre le problème. Au fait, au lieu de 'USE INDEX', vous pouvez essayer' FORCE INDEX'. – Karolis

0

Vous pouvez essayer 2 choses:

  • Un, avec des indices sur:

    • (forum_id, sticky, replied_at)
    • (other_forum_id, sticky, replied_at)

soit avec votre demande originale ou la suggestion de Karolis ou

  • Deux, avec des indices sur:

    • (forum_id)
    • (other_forum_id)
    • (sticky, replied_at)