Pokud to potřebujete udělat milionkrát, je tu spousta problémů s výkonem.
-
Připravujete stejný příkaz SQL znovu a znovu, milionkrát. Bylo by lepší to připravit jednou a spustit milionkrát.
-
Odpojujete se od databáze při každém volání funkce po jediném dotazu. To znamená, že se musíte pokaždé znovu připojit a veškeré informace uložené v mezipaměti budou zahozeny. Nedělejte to, nechte to připojené.
-
Po každém řádku se zavazujete. Tím se věci zpomalí. Místo toho proveďte potvrzení po provedení dávky.
-
Select + update nebo insert lze pravděpodobně provést jako jeden upsert.
-
To, že toho do dočasné tabulky vkládáte tolik, je pravděpodobně problém s výkonem.
-
Pokud má tabulka příliš mnoho indexů, může to zpomalit vkládání. Někdy je nejlepší indexy vypustit, provést velkou dávkovou aktualizaci a znovu je vytvořit.
-
Protože vkládáte hodnoty přímo do svého SQL, váš SQL je otevřen útoku SQL injection .
Místo toho...
- Používejte připravené příkazy a parametry vazby
- Nechte databázi připojenou
- Hromadné aktualizace
- Potvrdit pouze na konci běhu aktualizací
- Proveďte všechny výpočty v
UPDATE
spíše nežSELECT + math + UPDATE
. - Namísto
SELECT
použijte "UPSERT". potéUPDATE
neboINSERT
Nejprve připravená prohlášení. Ty umožňují MySQL jednou zkompilovat příkaz a poté jej znovu použít. Cílem je napsat prohlášení se zástupnými symboly pro hodnoty.
select id, position, impressions, clicks, ctr
from temp
where profile_id=%s and
keyword=%s and
landing_page=%s
Potom to provedete s hodnotami jako argumenty, nikoli jako součást řetězce.
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)
)
To umožňuje databázi uložit připravený příkaz do mezipaměti a nemusí jej pokaždé znovu kompilovat. Také se vyhne útoku SQL injection, kdy chytrý útočník může vytvořit hodnotu, která je ve skutečnosti spíše SQL jako " MORE SQL HERE "
. Je to velmi, velmi, velmi častá bezpečnostní díra.
Poznámka:Možná budete muset použít vlastní MySQL Knihovna databáze Python pro získání pravdivých připravených příkazů . Příliš si s tím nedělejte starosti, používání připravených příkazů není vaším největším výkonnostním problémem.
Dále to, co v podstatě děláte, je přidání do existujícího řádku, nebo pokud neexistuje žádný existující řádek, vložení nového. To lze provést efektivněji v jediném příkazu pomocí UPSERT
, kombinovaný INSERT
a UPDATE
. MySQL to má jako INSERT ... ON DUPLICATE KEY UPDATE
.
Chcete-li vidět, jak se to dělá, můžeme napsat váš SELECT then UPDATE
jako jeden UPDATE
. Výpočty se provádějí v 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
Váš INSERT zůstává stejný...
insert into temp
(profile_id, landing_page, keyword, position, impressions, clicks, ctr)
values (%s, %s, %s, %s, %s, %s, %s)
Spojte je do jednoho INSERT ON DUPLICATE KEY UPDATE.
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)
To závisí na tom, jak jsou definovány klíče tabulky. Pokud máte unique( profile_id, landing_page, keyword )
pak by to mělo fungovat stejně jako váš kód.
I když nemůžete udělat upsert, můžete odstranit SELECT
pomocí UPDATE
, zkontroluje, zda něco aktualizoval a zda neprovedl INSERT
.
Provádějte aktualizace hromadně. Místo volání podprogramu, který provede jednu aktualizaci a provede potvrzení, předejte mu velký seznam věcí, které mají být aktualizovány, a pracujte na nich ve smyčce. Můžete dokonce využít výhod executemany
spustit stejný příkaz s více hodnotami. Poté potvrďte.
Možná budete moci provést UPSERT
hromadně. INSERT
může mít více řádků najednou. Například tím vložíte tři řádky.
insert into whatever
(foo, bar, baz)
values (1, 2, 3),
(4, 5, 6),
(7, 8, 9)
Pravděpodobně můžete udělat totéž pomocí INSERT ON DUPLICATE KEY UPDATE
snížení režijních nákladů na komunikaci s databází. Příklad viz tento příspěvek
(v PHP, ale měli byste být schopni se přizpůsobit).
To obětuje vrácení ID posledního vloženého řádku, ale to jsou konce.