sql >> Databasteknik >  >> RDS >> Mysql

Snabba upp MySQL Update/Insert Statement

Det finns ett gäng prestandaproblem här om du behöver göra det miljontals gånger.

  • Du förbereder samma SQL-sats om och om igen, miljontals gånger. Det skulle fungera bättre att förbereda det en gång och köra det miljontals gånger.

  • Du kopplar från databasen vid varje funktionsanrop efter en enda fråga. Det betyder att du måste ansluta igen varje gång och all cachad information slängs. Gör inte det, låt den vara ansluten.

  • Du binder efter varje rad. Detta kommer att sakta ner saker och ting. Bekräfta istället efter att ha gjort en batch.

  • Välj + uppdatering eller infogning kan antagligen göras som en enda uppgradering.

  • Att du infogar så mycket i en temporär tabell är förmodligen ett prestandaproblem.

  • Om tabellen har för många index som kan göra insättningar långsammare. Ibland är det bäst att släppa index, göra en stor batchuppdatering och återskapa dem.

  • Eftersom du lägger in värden direkt i din SQL är din SQL öppen för en SQL-injektionsattack .

Istället...

  • Använd förberedda satser och bindningsparametrar
  • Lämna databasen ansluten
  • Gör flera uppdateringar samtidigt
  • Bekräfta endast i slutet av en serie uppdateringar
  • Gör all matematik i UPDATE istället för SELECT + math + UPDATE .
  • Använd en "UPSERT" istället för SELECT sedan UPDATE eller INSERT

Först och främst förberedda uttalanden. Dessa låter MySQL kompilera uttalandet en gång och sedan återanvända det. Tanken är att du skriver ett uttalande med platshållare för värdena.

select id, position, impressions, clicks, ctr
from temp
where profile_id=%s and
      keyword=%s and 
      landing_page=%s

Sedan kör du det med värdena som argument, inte som en del av strängen.

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)
)

Detta gör att databasen kan cachelagra den förberedda satsen och inte behöva kompilera om den varje gång. Det undviker också en SQL-injektionsattack där en smart angripare kan skapa ett värde som faktiskt är mer SQL som " MORE SQL HERE " . Det är ett väldigt, väldigt, väldigt vanligt säkerhetshål.

Observera att du kan behöva använda MySQL:s egna Python-databasbibliotek för att få riktiga förberedda uttalanden . Oroa dig inte för mycket, att använda förberedda uttalanden är inte ditt största prestandaproblem.

Därefter, vad du i princip gör är att lägga till en befintlig rad, eller om det inte finns någon befintlig rad, infoga en ny. Detta kan göras mer effektivt i en enda sats med en UPSERT , en kombinerad INSERT och UPDATE . MySQL har det som INSERT ... ON DUPLICATE KEY UPDATE .

För att se hur detta görs kan vi skriva din SELECT then UPDATE som en enda UPDATE . Beräkningarna görs 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 förblir densamma...

    insert into temp
        (profile_id, landing_page, keyword, position, impressions, clicks, ctr)
        values (%s, %s, %s, %s, %s, %s, %s)

Kombinera dem till en 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)

Detta beror på vad tabellens nycklar definieras som. Om du har unique( profile_id, landing_page, keyword ) då borde det fungera på samma sätt som din kod.

Även om du inte kan göra uppsvinget kan du ta bort SELECT genom att prova UPDATE , kontrollera om den uppdaterade något och om den inte gjorde en INSERT .

Gör uppdateringarna samtidigt. Istället för att anropa en subrutin som gör en uppdatering och begår, skicka den en stor lista med saker som ska uppdateras och arbeta med dem i en slinga. Du kan till och med dra nytta av executemany att köra samma programsats med flera värden. Begå sedan.

Du kanske kan göra UPSERT i bulk. INSERT kan ta flera rader samtidigt. Detta infogar till exempel tre rader.

insert into whatever
    (foo, bar, baz)
values (1, 2, 3),
       (4, 5, 6), 
       (7, 8, 9)

Du kan sannolikt göra detsamma med din INSERT ON DUPLICATE KEY UPDATE minska mängden overhead för att prata med databasen. Se det här inlägget för ett exempel (i PHP, men du bör kunna anpassa dig).

Detta offrar att returnera ID:t för den senast infogade raden, men det är pauserna.




  1. Snabb delad partitionering

  2. Behöver hjälp med att skapa en dynamisk meny

  3. Mysql:ERROR 1005 (HY000):Kan inte skapa tabellen "receitascakephp.recipes" (fel:150)

  4. Finns det en SELECT ... INTO OUTFILE-motsvarighet i SQL Server Management Studio?