Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Optimierung von ORDER BY

Dies ist eine sehr interessante Abfrage. Während der Optimierung können Sie viele neue Informationen darüber entdecken und verstehen, wie MySQL funktioniert. Ich bin mir nicht sicher, ob ich Zeit haben werde, alles im Detail auf einmal zu schreiben, aber ich kann nach und nach aktualisieren.

Warum es langsam ist

Grundsätzlich gibt es zwei Szenarien:ein schnelles und ein langsames .

schnell Szenario gehen Sie in einer vordefinierten Reihenfolge über eine Tabelle und holen wahrscheinlich gleichzeitig schnell einige Daten nach ID für jede Zeile aus anderen Tabellen. In diesem Fall hören Sie auf zu gehen, sobald Sie genügend Zeilen haben, die durch Ihre LIMIT-Klausel angegeben sind. Woher kommt die Bestellung? Aus einem B-Tree-Index, den Sie in der Tabelle haben, oder der Reihenfolge einer Ergebnismenge in einer Unterabfrage.

In einem langsamen In diesem Szenario haben Sie diese vordefinierte Reihenfolge nicht, und MySQL muss alle Daten implizit in eine temporäre Tabelle einfügen, die Tabelle nach einem Feld sortieren und das n zurückgeben Zeilen aus Ihrer LIMIT-Klausel. Wenn eines der Felder, die Sie in diese temporäre Tabelle einfügen, vom Typ TEXT (nicht VARCHAR) ist, versucht MySQL nicht einmal, diese Tabelle im RAM zu halten, sondern leert und sortiert sie auf der Festplatte (daher zusätzliche IO-Verarbeitung).

Als erstes zu beheben

Es gibt viele Situationen, in denen Sie keinen Index erstellen können, der es Ihnen erlaubt, seiner Reihenfolge zu folgen (wenn Sie zum Beispiel ORDER BY-Spalten aus verschiedenen Tabellen verwenden), daher lautet die Faustregel in solchen Situationen, die von MySQL abgelegten Daten zu minimieren in der temporären Tabelle. Wie kannst du das machen? Sie wählen nur Bezeichner der Zeilen in einer Unterabfrage aus und nachdem Sie die IDs haben, verknüpfen Sie die IDs mit der Tabelle selbst und anderen Tabellen, um den Inhalt abzurufen. Das heißt, Sie erstellen eine kleine Tabelle mit einer Bestellung und verwenden dann das schnelle Szenario. (Dies widerspricht leicht SQL im Allgemeinen, aber jede SQL-Variante hat ihre eigenen Mittel, um Abfragen auf diese Weise zu optimieren).

Zufälligerweise ist Ihr SELECT -- everything is ok here sieht komisch aus, da es die erste Stelle ist, wo es nicht ok ist.

SELECT p.*
    , u.name user_name, u.status user_status
    , c.name city_name, t.name town_name, d.name dist_name
    , pm.meta_name, pm.meta_email, pm.meta_phone
    , (SELECT concat("{", 
        '"id":"', pc.id, '",', 
        '"content":"', replace(pc.content, '"', '\\"'), '",', 
        '"date":"', pc.date, '",', 
        '"user_id":"', pcu.id, '",', 
        '"user_name":"', pcu.name, '"}"') last_comment_json 
        FROM post_comments pc 
        LEFT JOIN users pcu ON (pcu.id = pc.user_id) 
        WHERE pc.post_id = p.id
        ORDER BY pc.id DESC LIMIT 1) AS last_comment
FROM (
    SELECT id
    FROM posts p
    WHERE p.status = 'published'
    ORDER BY 
        (CASE WHEN p.created_at >= unix_timestamp(now() - INTERVAL p.reputation DAY) 
            THEN +p.reputation ELSE NULL END) DESC, 
        p.id DESC
    LIMIT 0,10
) ids
JOIN posts p ON ids.id = p.id  -- mind the join for the p data
LEFT JOIN users u ON (u.id = p.user_id)
LEFT JOIN citys c ON (c.id = p.city_id)
LEFT JOIN towns t ON (t.id = p.town_id)
LEFT JOIN dists d ON (d.id = p.dist_id)
LEFT JOIN post_metas pm ON (pm.post_id = p.id)
;

Das ist der erste Schritt, aber schon jetzt können Sie sehen, dass Sie diese nutzlosen LEFT JOINS und JSON-Serialisierungen für die Zeilen, die Sie nicht benötigen, nicht machen müssen. (Ich habe GROUP BY p.id übersprungen , da ich nicht sehe, welcher LEFT JOIN zu mehreren Zeilen führen könnte, führen Sie keine Aggregation durch).

noch zu schreiben:

  • Indizes
  • CASE-Klausel neu formulieren (UNION ALL verwenden)
  • wahrscheinlich einen Index erzwingen