Der er en masse ydeevneproblemer her, hvis du har brug for at gøre dette millioner af gange.
-
Du forbereder den samme SQL-sætning igen og igen, millioner af gange. Det ville fungere bedre at forberede det én gang og udføre det millioner af gange.
-
Du afbryder forbindelsen til databasen ved hvert funktionskald efter en enkelt forespørgsel. Det betyder, at du skal genoprette forbindelsen hver gang, og enhver cachelagret information bliver smidt væk. Gør det ikke, lad den være tilsluttet.
-
Du forpligter dig efter hver række. Dette vil bremse tingene. I stedet skal du forpligte dig efter at have udført en batch.
-
Vælg + opdateringen eller indsættelsen kan sandsynligvis udføres som en enkelt upsert.
-
At du indsætter så meget i en vikartabel er sandsynligvis et problem med ydeevnen.
-
Hvis tabellen har for mange indekser, kan det forsinke indsættelser. Nogle gange er det bedst at droppe indekser, lave en stor batchopdatering og genskabe dem.
-
Fordi du sætter værdier direkte i din SQL, er din SQL åben for et SQL-injektionsangreb .
I stedet...
- Brug forberedte udsagn og bindeparametre
- Lad databasen være tilsluttet
- Foretag opdateringer på én gang
- Forpligtelse kun i slutningen af en række opdateringer
- Foretag alt regnestykket i
UPDATE
i stedet forSELECT + math + UPDATE
. - Brug en "UPSERT" i stedet for
SELECT
derefterUPDATE
ellerINSERT
Først forberedte erklæringer. Disse lader MySQL kompilere erklæringen én gang og genbruge den derefter. Ideen er, at du skriver en erklæring med pladsholdere for værdierne.
select id, position, impressions, clicks, ctr
from temp
where profile_id=%s and
keyword=%s and
landing_page=%s
Så udfører du det med værdierne som argumenter, ikke som en del af strengen.
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)
)
Dette gør det muligt for databasen at cache den forberedte sætning og ikke at skulle kompilere den igen hver gang. Det undgår også et SQL-injektionsangreb, hvor en klog angriber kan lave en værdi, der faktisk er mere SQL som " MORE SQL HERE "
. Det er et meget, meget, meget almindeligt sikkerhedshul.
Bemærk, du skal muligvis bruge MySQL's egen Python-databasebibliotek for at få ægte forberedte udsagn . Du skal ikke bekymre dig for meget om det, at bruge forberedte udsagn er ikke dit største præstationsproblem.
Dernæst er det, du grundlæggende gør, at tilføje til en eksisterende række, eller hvis der ikke er nogen eksisterende række, indsætte en ny. Dette kan gøres mere effektivt i en enkelt sætning med en UPSERT
, en kombineret INSERT
og UPDATE
. MySQL har det som INSERT ... ON DUPLICATE KEY UPDATE
.
For at se, hvordan dette gøres, kan vi skrive din SELECT then UPDATE
som en enkelt UPDATE
. Beregningerne udføres i 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
Din INSERT forbliver den samme...
insert into temp
(profile_id, landing_page, keyword, position, impressions, clicks, ctr)
values (%s, %s, %s, %s, %s, %s, %s)
Kombiner dem til én INDSÆT PÅ DUBLIKAT NØGLEOPDATERING.
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)
Dette afhænger af, hvad tabellens nøgler er defineret som. Hvis du har unique( profile_id, landing_page, keyword )
så skulle det fungere på samme måde som din kode.
Selvom du ikke kan gøre opskæringen, kan du fjerne SELECT
ved at prøve UPDATE
, tjekker om den opdaterede noget, og om den ikke lavede en INSERT
.
Foretag masseopdateringerne. I stedet for at kalde en underrutine, som opdaterer og forpligter, skal du sende den en stor liste over ting, der skal opdateres, og arbejde på dem i en løkke. Du kan endda drage fordel af executemany
at køre den samme sætning med flere værdier. Så forpligt dig.
Du kan muligvis udføre UPSERT
i løs vægt. INSERT
kan tage flere rækker på én gang. For eksempel indsætter dette tre rækker.
insert into whatever
(foo, bar, baz)
values (1, 2, 3),
(4, 5, 6),
(7, 8, 9)
Du kan sandsynligvis gøre det samme med din INSERT ON DUPLICATE KEY UPDATE
reducere mængden af overhead for at tale med databasen. Se dette indlæg for et eksempel
(i PHP, men du burde være i stand til at tilpasse).
Dette ofrer returnering af ID'et for den sidst indsatte række, men det er pauserne.