Er zijn hier een heleboel prestatieproblemen als je dit miljoenen keren moet doen.
-
U bereidt dezelfde SQL-instructie steeds opnieuw voor, miljoenen keren. Het zou beter zijn om het één keer voor te bereiden en het miljoenen keren uit te voeren.
-
U verbreekt de verbinding met de database bij elke functieaanroep na een enkele query. Dat betekent dat u elke keer opnieuw verbinding moet maken en dat alle informatie in de cache wordt weggegooid. Doe dat niet, laat het aangesloten.
-
Je commiteert je na elke rij. Dit zal de zaken vertragen. Leg in plaats daarvan vast na het uitvoeren van een batch.
-
De select + update of insert kan waarschijnlijk als een enkele upsert worden gedaan.
-
Dat je zoveel in een tijdelijke tabel invoert, is waarschijnlijk een prestatieprobleem.
-
Als de tabel te veel indexen heeft, kan het invoegen vertragen. Soms is het het beste om indexen te verwijderen, een grote batch-update uit te voeren en ze opnieuw te maken.
-
Omdat u waarden rechtstreeks in uw SQL plaatst, staat uw SQL open voor een SQL-injectieaanval .
In plaats daarvan...
- Gebruik voorbereide instructies en bindparameters
- De database aangesloten laten
- Voer updates in bulk uit
- Alleen vastleggen aan het einde van een reeks updates
- Doe alle wiskunde in de
UPDATE
in plaats vanSELECT + math + UPDATE
. - Gebruik een "UPSERT" in plaats van
SELECT
danUPDATE
ofINSERT
Ten eerste, voorbereide verklaringen. Deze laten MySQL de instructie één keer compileren en vervolgens hergebruiken. Het idee is dat je een verklaring schrijft met tijdelijke aanduidingen voor de waarden.
select id, position, impressions, clicks, ctr
from temp
where profile_id=%s and
keyword=%s and
landing_page=%s
Dan voer je dat uit met de waarden als argumenten, niet als onderdeel van de string.
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)
)
Hierdoor kan de database de voorbereide instructie in de cache opslaan en hoeft deze niet elke keer opnieuw te compileren. Het vermijdt ook een SQL-injectieaanval waarbij een slimme aanvaller een waarde kan maken die eigenlijk meer SQL-achtig is " MORE SQL HERE "
. Het is een heel, heel, heel veel voorkomend beveiligingslek.
Let op, u moet mogelijk MySQL's eigen Python-databasebibliotheek om echte voorbereide instructies te krijgen . Maak je er niet al te veel zorgen over, het gebruik van voorbereide verklaringen is niet je grootste prestatieprobleem.
Wat u vervolgens doet, is toevoegen aan een bestaande rij, of, als er geen bestaande rij is, een nieuwe invoegen. Dit kan efficiënter in een enkele instructie met een UPSERT
, een gecombineerde INSERT
en UPDATE
. MySQL heeft het als INSERT ... ON DUPLICATE KEY UPDATE
.
Om te zien hoe dit wordt gedaan, kunnen we uw SELECT then UPDATE
write schrijven als een enkele UPDATE
. De berekeningen worden gedaan in de 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
Uw INSERT blijft hetzelfde...
insert into temp
(profile_id, landing_page, keyword, position, impressions, clicks, ctr)
values (%s, %s, %s, %s, %s, %s, %s)
Combineer ze in één 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)
Dit hangt af van hoe de toetsen van de tabel zijn gedefinieerd. Als u unique( profile_id, landing_page, keyword )
. heeft dan zou het hetzelfde moeten werken als uw code.
Zelfs als je de upsert niet kunt doen, kun je de SELECT
. elimineren door de UPDATE
. te proberen , controleren of het iets heeft bijgewerkt en of het geen INSERT
. heeft gedaan .
Voer de updates in bulk uit. In plaats van een subroutine aan te roepen die één update uitvoert en vastlegt, geef je hem een grote lijst met dingen die moeten worden bijgewerkt en werk je er in een lus aan. U kunt zelfs profiteren van executemany
om dezelfde instructie met meerdere waarden uit te voeren. Leg je dan vast.
Mogelijk kunt u de UPSERT
massaal. INSERT
kan meerdere rijen tegelijk bevatten. Dit voegt bijvoorbeeld drie rijen in.
insert into whatever
(foo, bar, baz)
values (1, 2, 3),
(4, 5, 6),
(7, 8, 9)
U kunt waarschijnlijk hetzelfde doen met uw INSERT ON DUPLICATE KEY UPDATE
het verminderen van de hoeveelheid overhead om met de database te praten. Zie dit bericht voor een voorbeeld
(in PHP, maar je zou je moeten kunnen aanpassen).
Dit offert op het retourneren van de ID van de laatst ingevoegde rij, maar dat zijn de pauzes.