Mysql
 sql >> Baza danych >  >> RDS >> Mysql

Optymalizacja ORDER BY

To bardzo ciekawe zapytanie. Podczas jego optymalizacji możesz odkryć i zrozumieć wiele nowych informacji na temat działania MySQL. Nie jestem pewien, czy będę miał czas na opisanie wszystkiego od razu, ale mogę stopniowo aktualizować.

Dlaczego jest wolny

Istnieją zasadniczo dwa scenariusze:szybkie i zwolnienie .

W szybko Scenariusz, że chodzisz w jakiejś predefiniowanej kolejności nad tabelą i prawdopodobnie w tym samym czasie szybko pobierasz dane według id dla każdego wiersza z innych tabel. W takim przypadku przestaniesz chodzić, gdy tylko będziesz mieć wystarczającą liczbę wierszy określonych w klauzuli LIMIT. Skąd pochodzi zamówienie? Z indeksu b-drzewa, który masz w tabeli lub kolejności zestawu wyników w podzapytaniu.

W wolno scenariusz nie masz takiej predefiniowanej kolejności, a MySQL musi pośrednio umieścić wszystkie dane w tabeli tymczasowej, posortować tabelę według jakiegoś pola i zwrócić n wierszy z klauzuli LIMIT. Jeśli którekolwiek z pól, które umieściłeś w tej tabeli tymczasowej, jest typu TEXT (nie VARCHAR), MySQL nawet nie próbuje trzymać tej tabeli w pamięci RAM i opróżnia ją i sortuje na dysku (stąd dodatkowe przetwarzanie we/wy).

Pierwsza rzecz do naprawienia

Jest wiele sytuacji, w których nie można zbudować indeksu, który pozwoli na zachowanie jego kolejności (np. podczas tworzenia kolumn ORDER BY z różnych tabel), więc praktyczną zasadą w takich sytuacjach jest minimalizacja danych, które umieści MySQL w tabeli tymczasowej. Jak możesz to zrobić? Wybierasz tylko identyfikatory wierszy w podzapytaniu, a po ich uzyskaniu łączysz identyfikatory z samą tabelą i innymi tabelami, aby pobrać zawartość. To znaczy, że robisz mały stolik z zamówieniem, a następnie używasz szybkiego scenariusza. (To jest nieco sprzeczne z SQL ogólnie, ale każdy rodzaj SQL ma swoje własne sposoby na optymalizację zapytań w ten sposób).

Przypadkowo, twój SELECT -- everything is ok here wygląda zabawnie, ponieważ jest to pierwsze miejsce, w którym nie jest w porządku.

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)
;

To pierwszy krok, ale nawet teraz widać, że nie trzeba robić tych bezużytecznych LEFT JOINS i serializacji json dla wierszy, których nie potrzebujesz. (Pominąłem GROUP BY p.id , ponieważ nie widzę, który LEFT JOIN może skutkować kilkoma wierszami, nie wykonuje się żadnej agregacji).

jeszcze o czym pisać:

  • indeksy
  • przeformułuj klauzulę CASE (użyj UNION ALL)
  • prawdopodobnie wymuszając indeks


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Usuń wiele wierszy z tabeli za pomocą identyfikatora w Mysql

  2. Jak importować i eksportować bazę danych przez phpMyAdmin (błąd „Odmowa dostępu, utwórz bazę danych db_name”)

  3. Jak zablokować odczyt/zapis w tabelach MySQL, aby móc wybierać, a następnie wstawiać bez innych programów odczytujących/zapisujących do bazy danych?

  4. Jak prawidłowo oczyścić dane otrzymane z obszaru tekstowego podczas wyprowadzania ich z powrotem do obszaru tekstowego?

  5. MySQL:kolumna obliczeniowa