Istnieje wiele problemów z wydajnością, jeśli musisz to zrobić miliony razy.
-
Przygotowujesz tę samą instrukcję SQL w kółko, miliony razy. Lepiej byłoby przygotować go raz i wykonać miliony razy.
-
Rozłączasz się z bazą danych przy każdym wywołaniu funkcji po jednym zapytaniu. Oznacza to, że za każdym razem musisz łączyć się ponownie, a wszelkie informacje z pamięci podręcznej są wyrzucane. Nie rób tego, pozostaw to podłączone.
-
Zobowiązujesz się po każdym rzędzie. To spowolni sprawę. Zamiast tego zatwierdź po wykonaniu partii.
-
Wybierz + aktualizacja lub wstaw prawdopodobnie można wykonać jako pojedynczy upsert.
-
To, że wstawiasz tak dużo do tabeli tymczasowej, prawdopodobnie ma problem z wydajnością.
-
Jeśli tabela ma zbyt wiele indeksów, które mogą spowolnić wstawianie. Czasami najlepiej jest usunąć indeksy, przeprowadzić dużą aktualizację zbiorczą i odtworzyć je.
-
Ponieważ umieszczasz wartości bezpośrednio w swoim SQL, Twój SQL jest otwarty na atak wstrzykiwania SQL .
Zamiast tego...
- Użyj przygotowanych instrukcji i parametrów wiązania
- Pozostaw bazę danych podłączoną
- Dokonuj zbiorczych aktualizacji
- Zatwierdzaj tylko pod koniec serii aktualizacji
- Wykonaj całą matematykę w
UPDATE
zamiastSELECT + math + UPDATE
. - Użyj „UPSERT” zamiast
SELECT
następnieUPDATE
lubINSERT
Po pierwsze, przygotowane zestawienia. Pozwalają one MySQL raz skompilować instrukcję, a następnie ponownie ją wykorzystać. Pomysł polega na napisaniu oświadczenia z symbolami zastępczymi dla wartości.
select id, position, impressions, clicks, ctr
from temp
where profile_id=%s and
keyword=%s and
landing_page=%s
Następnie wykonujesz to, używając wartości jako argumentów, a nie jako części ciągu.
self.cursor.execute(
'select id, position, impressions, clicks, ctr from temp where profile_id=%s and keyword=%s and landing_page=%s',
(profile_id, keyword, landing_page)
)
Dzięki temu baza danych może buforować przygotowaną instrukcję i nie musi za każdym razem jej ponownie kompilować. Pozwala również uniknąć ataku typu SQL injection, w którym sprytny atakujący może stworzyć wartość, która jest w rzeczywistości bardziej SQL, jak " MORE SQL HERE "
. To bardzo, bardzo, bardzo powszechna luka w zabezpieczeniach.
Uwaga, może być konieczne użycie własnego MySQL Biblioteka bazy danych Pythona do uzyskiwania prawdziwych przygotowanych instrukcji . Nie przejmuj się tym zbytnio, używanie przygotowanych instrukcji nie jest twoim największym problemem z wydajnością.
Następnie to, co w zasadzie robisz, to dodawanie do istniejącego wiersza lub, jeśli nie ma istniejącego wiersza, wstawianie nowego. Można to zrobić wydajniej za pomocą pojedynczej instrukcji z UPSERT
, połączony INSERT
i UPDATE
. MySQL ma to jako INSERT ... ON DUPLICATE KEY UPDATE
.
Aby zobaczyć, jak to się robi, możemy napisać Twój SELECT then UPDATE
jako pojedyncza UPDATE
. Obliczenia są wykonywane w SQL.
update temp
set impressions = impressions + %s,
clicks = clicks + %s,
ctr = (ctr + %s / 2)
where profile_id=%s and
keyword=%s and
landing_page=%s
Twoja INSERT pozostaje taka sama...
insert into temp
(profile_id, landing_page, keyword, position, impressions, clicks, ctr)
values (%s, %s, %s, %s, %s, %s, %s)
Połącz je w jedną WSTAWIĆ O AKTUALIZACJI DUPLIKOWANEGO KLUCZA.
insert into temp
(profile_id, landing_page, keyword, position, impressions, clicks, ctr)
values (%s, %s, %s, %s, %s, %s, %s)
on duplicate key update
update temp
set impressions = impressions + %s,
clicks = clicks + %s,
ctr = (ctr + %s / 2)
Zależy to od definicji kluczy tabeli. Jeśli masz unique( profile_id, landing_page, keyword )
wtedy powinien działać tak samo jak twój kod.
Nawet jeśli nie możesz zrobić upsert, możesz wyeliminować SELECT
próbując UPDATE
, sprawdzając, czy coś zaktualizowało i czy nie wykonał INSERT
.
Wykonuj aktualizacje zbiorczo. Zamiast wywoływać podprogram, który wykonuje jedną aktualizację i zatwierdza, przekaż mu dużą listę rzeczy do zaktualizowania i pracuj nad nimi w pętli. Możesz nawet skorzystać z executemany
uruchomić tę samą instrukcję z wieloma wartościami. Następnie zatwierdź.
Możesz być w stanie wykonać UPSERT
hurtowo. INSERT
może zająć wiele rzędów na raz. Na przykład wstawia to trzy wiersze.
insert into whatever
(foo, bar, baz)
values (1, 2, 3),
(4, 5, 6),
(7, 8, 9)
Prawdopodobnie możesz zrobić to samo ze swoim INSERT ON DUPLICATE KEY UPDATE
zmniejszenie narzutu na rozmowę z bazą danych. Zobacz ten post na przykład
(w PHP, ale powinieneś być w stanie się dostosować).
Poświęca to zwracanie identyfikatora ostatniego wstawionego wiersza, ale to są przerwy.