Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

> [mySQL] Sortowanie wg kolumny COUNT
starcode
post
Post #1





Grupa: Zarejestrowani
Postów: 28
Pomógł: 0
Dołączył: 18.06.2006

Ostrzeżenie: (0%)
-----


Witam.

Borykam się ostatnio z wydajnością zapytania, w którym występuje złączenie dwóch dużych tabel (pierwsza 2mln rekordów, druga 4mln rekordów).

Zapytanie podstawowo wygląda tak:
  1. SELECT * , p.`product_id` AS `product_id` , COUNT( DISTINCT (o.`store_id` ) ) AS `liczba_produktow` , MIN( o.`product_offer_price` ) AS `cena`
  2. FROM `products` p INNER JOIN `products_offers` o ON ( o.`product_id` = p.`product_id` AND o.`product_offer_status` = '1' )
  3. WHERE p.`product_navi_3` = '1158'
  4. GROUP BY p.`product_id`
  5. ORDER BY `liczba_produktow` DESC
  6. LIMIT 0 , 100


Na wszelkie pola biorące udział w warunkach zapytania (czyli product_id, store_id, product_offer_status, product_navi_3) są nałożone indeksy (indeksu nie ma jedynie na kolumnie ceny - product_offer_price). Ale nie to jest problemem. Z przeprowadzonych testów, spowolnienie (czy raczej zasobożerność) zwiększa klauzula ORDER BY.
Gdy klauzuli ORDER BY nie ma (tj. sortowanie nie odbywa się po kolumnie `liczba_produktow` lub `cena`) mySQL przegląda jedynie rekordy, które spełniają warunek WHERE (czyli product_navi_3 = 1158), w momencie gdy ORDER BY jest - mysql przegląda wszystkie rekordy w tabeli (2mln!).
Dzieje się tak dlatego, że nie ma indeksów na sortowanych kolumnach - ale nie ma (chyba?) możliwości nałożenia indeksu na kolumny, które są obliczane w "locie".

Dlatego też wykombinowałem SELECT z SELECT'a, który wygląda mniej więcej tak:
  1. SELECT *
  2. FROM (
  3. SELECT p.`product_name` , p.`product_id` , COUNT( DISTINCT (o.`store_id` ) ) AS `liczba_produktow` , MIN( o.`product_offer_price` ) AS `cena`
  4. FROM `products` p INNER JOIN `products_offers` o ON ( o.`product_id` = p.`product_id` AND o.`product_offer_status` = '1' AND p.`product_navi_1` = '1154' )
  5. GROUP BY p.`product_id`
  6. LIMIT 0 , 5000
  7. )res
  8. ORDER BY res.`liczba_produktow` DESC
  9. LIMIT 0 , 100


Zapytanie zagnieżdzone pobiera tylko tyle rekordów ile jest w limicie (tj. nie przeszukuje całej tabeli). Wyszedłem z założenia, że mogę pobrać wszystkie rekordy i dopiero je posortować - w danym indeksie (kategorii zazwyczaj nie ma więcej niż 5000 rekordów), więc stosunkowo małe porcje danych są pobierane.
Niestety w zapytaniu dalej, dodanie ORDER BY powoduje, że mimo wszystko pobierane (przeglądane) są wszystkie dane z tabeli.

Czy jest jakieś rozwiązanie, aby mySQL na siłę nie próbował pobierać wszystkich danych ponownie, tylko wykonał sortowanie na tym mniejszym już zbiorze (z zagnieżdzonego selecta)?

Z góry dzięki za pomoc...

Ten post edytował starcode 2.05.2013, 12:18:53
Go to the top of the page
+Quote Post
 
Start new topic
Odpowiedzi
pmir13
post
Post #2





Grupa: Zarejestrowani
Postów: 282
Pomógł: 89
Dołączył: 12.04.2011

Ostrzeżenie: (0%)
-----


Ile jest produktów spełniających ten warunek product_navi_3 = 1158? Najwyraźniej mimo istnienia indeksu i wypisania jako możliwy do użycia optimizer nie zdecydował się go użyć. Jedną z przyczyn może być to, że jeśli tych produktów jest bardzo dużo, to pełny skan tabeli może być szybszy niż szukanie przez indeks. Dzieje się tak dlatego, że sekwencyjne czytanie z dysku jest o rząd wielkości szybsze niż losowe, a w zapytaniu z tej tabeli potrzebujesz więcej niż tylko product_id i product_navi_3, co sprawia że ten indeks nie jest "covering" (jaki jest polski odpowiednik?), czyli nie zawiera wszystkich kolumn, które trzeba byłoby przeczytać. Więc i tak jeśli ten indeks miałby być użyty to zamiast przeczytać wszystkie wartości bezpośrednio z indeksu, trzeba by losowo skakać po tabeli by dostać brakujące w indeksie kolumny. Jeśli tabela jest w InnoDb, to indeks na product_navi_3 zawiera również product_id, więc ograniczenie się do tych dwóch kolumn w select powinno pomóc. Dla tabel MyISAM potrzebny byłby podwójny indeks (product_navi_3, product_id).
Oczywiście można też spróbować użyć FORCE INDEX( product_navi_3 ) po nazwie tabeli products i zobaczyć rezultaty czy faktycznie byłoby dzięki temu szybciej.
ANALYZE TABLE dla obu tabel też mogłoby pomóc jeśli nie było niedawno robione, dla odświeżenia statystyk indeksów. Jeżeli nie są zbyt aktualne, a w międzyczasie było sporo zmian w tabeli, to też może być przyczyną tego, że optimizer nie decyduje się użyć odpowiedniego indeksu.

Ten post edytował pmir13 3.05.2013, 09:35:41
Go to the top of the page
+Quote Post

Posty w temacie


Reply to this topicStart new topic
2 Użytkowników czyta ten temat (2 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Aktualny czas: 18.10.2025 - 06:41