2015-10-11 5 views
5

Moja strona internetowa zawiera ponad 20 000 000 wpisów, wpisów ma kategorie (FK) i znaczniki (M2M). Jeśli chodzi o kwerendę nawet jak SELECT id FROM table ORDER BY id LIMIT 1000000, 10 MySQL musi skanować 1000010 wierszy, ale to jest naprawdę niedopuszczalnie powolne (i pks, indeksy, sprzężenia itp itp. Nie pomagają tutaj wiele, wciąż 1000010 wierszy). Więc staram się przyspieszyć paginacji przechowując liczbę wierszy i liczbę wierszy z wyzwalaczy, takich jak ten:Czy przechowywanie stronicowania i liczby wierszy do przechowywania stron jest złe?

DELIMITER // 
CREATE TRIGGER @trigger_name 
AFTER INSERT 
ON entry_table FOR EACH ROW 
BEGIN 
    UPDATE category_table SET row_count = (@rc := row_count + 1) 
    WHERE id = NEW.category_id; 
    NEW.row_number_in_category = @rc; 
END // 

A potem mogę po prostu:

SELECT * 
FROM entry_table 
WHERE row_number_in_category > 10 
ORDER BY row_number_in_category 
LIMIT 10 

(teraz tylko 10 wierszy skanowana i dlatego wybiera się płonący szybko, chociaż wstawki są wolniejsze, ale są rzadkie w porównaniu do wybranych, więc jest w porządku)

Czy to złe podejście i czy istnieją dobre alternatywy?

+2

Brzmi jak fajna optymalizacja; pod warunkiem, że wyeliminowałeś wszystkie inne przyczyny perfekcji (takie jak indeksy), ten rodzaj denormalizacji jest akceptowalny, ale rozważ przechowywanie tych informacji w osobnej tabeli metadanych, aby utrzymać główny schemat "czysty". – Dai

+1

To dobry pomysł, ale prawdopodobnie nie jest to konieczne, ponieważ w odróżnieniu od Postgresql mysql obsługuje bardzo dobrze zliczanie (*) w tabelach indeksowanych. Zobacz moją odpowiedź tutaj, aby uzyskać więcej informacji http://stackoverflow.com/a/33006075/267540 – e4c5

+0

Należy tylko przeskanować te wiersze, jeśli "id" nie jest indeksowany. Rozwiązujesz niewłaściwy problem. – EJP

Odpowiedz

1

Chociaż podoba mi się rozwiązanie w pytaniu. Może to powodować pewne problemy, jeśli dane w tabeli entry_table zostały zmienione - być może usunięte lub przypisane do różnych kategorii w czasie.

Ogranicza również sposób sortowania danych, a metoda zakłada, że ​​dane są sortowane tylko według kolejności wstawiania. Objęcie wielu metod sortowania wymaga dodatkowych wyzwalaczy i danych podsumowania.

Alternatywnym sposobem dzielenia stron jest przesuwanie w przesunięciu względem pola, które sortujesz/paginacja, zamiast przesunięcia do parametru limitu.

Zamiast tego:

SELECT id FROM table ORDER BY id LIMIT 1000000, 10 

Zrób to - przy założeniu, że w tym scenariuszu, że ostatni wynik oglądany miał id 1000000

SELECT id FROM table WHERE id > 1000000 ORDER BY id LIMIT 0, 10 

Śledząc przesunięcie paginacji, może to przekazywane do kolejnych zapytań o dane i unika sortowania wierszy bazy danych, które nigdy nie będą częścią wyniku końcowego.

Jeśli naprawdę chciałeś tylko 10 wierszy z 20 milionów, możesz pójść dalej i zgadnąć, że następne 10 pasujących wierszy wystąpi w następnych 1000 ogólnych wyników. Być może z pewną logiką, aby powtórzyć zapytanie z większym dodatkiem, jeśli tak nie jest.

SELECT id FROM table WHERE id BETWEEN 1000000 AND 1001000 ORDER BY id LIMIT 0, 10 

To powinno być znacznie szybsze, ponieważ sortowanie prawdopodobnie będzie mogło ograniczyć wynik w jednym przebiegu.

+1

Warto zauważyć, że o ile identyfikatory nie są ciągłe i zaczynać od 1, 'LIMIT 1000000, 10' NIE jest tożsamy ​​z' id> 1000000 ... LIMIT 0, 10'. – Arth

+1

@Arth, Dzięki, tak, to prawda. Chociaż zasada działa - dwa zapytania w przykładzie mogą nie zwracać takich samych wyników, jak mówisz. Dobre wyjaśnienie. –