Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Beschleunigen Sie die MySQL Update/Insert-Anweisung

Es gibt hier eine Reihe von Leistungsproblemen, wenn Sie dies millionenfach tun müssen.

  • Sie bereiten immer und immer wieder dieselbe SQL-Anweisung vor, millionenfach. Es wäre besser, es einmal vorzubereiten und millionenfach auszuführen.

  • Sie trennen die Verbindung zur Datenbank bei jedem Funktionsaufruf nach einer einzigen Abfrage. Das bedeutet, dass Sie die Verbindung jedes Mal neu herstellen müssen und alle zwischengespeicherten Informationen weggeworfen werden. Tun Sie das nicht, lassen Sie es angeschlossen.

  • Sie verpflichten sich nach jeder Reihe. Dies wird die Dinge verlangsamen. Bestätigen Sie stattdessen, nachdem Sie einen Stapel ausgeführt haben.

  • Das Auswählen + Aktualisieren oder Einfügen kann wahrscheinlich als einzelnes Upsert erfolgen.

  • Dass Sie so viel in eine temporäre Tabelle einfügen, ist wahrscheinlich ein Leistungsproblem.

  • Wenn die Tabelle zu viele Indizes hat, kann das Einfügungen verlangsamen. Manchmal ist es am besten, Indizes zu löschen, eine große Stapelaktualisierung durchzuführen und sie neu zu erstellen.

  • Da Sie Werte direkt in Ihr SQL einfügen, ist Ihr SQL anfällig für einen SQL-Injection-Angriff .

Stattdessen...

  • Vorbereitete Anweisungen verwenden und Parameter binden
  • Lassen Sie die Datenbank verbunden
  • Massenweise Aktualisierungen durchführen
  • Nur am Ende eines Aktualisierungslaufs festschreiben
  • Machen Sie alle Berechnungen im UPDATE statt SELECT + math + UPDATE .
  • Verwenden Sie ein "UPSERT" anstelle von SELECT dann UPDATE oder INSERT

Zunächst einmal vorbereitete Erklärungen. Diese lassen MySQL die Anweisung einmal kompilieren und dann wiederverwenden. Die Idee ist, dass Sie eine Anweisung mit Platzhaltern für die Werte schreiben.

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

Dann führen Sie das mit den Werten als Argumente aus, nicht als Teil des Strings.

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

Dadurch kann die Datenbank die vorbereitete Anweisung zwischenspeichern und muss sie nicht jedes Mal neu kompilieren. Es vermeidet auch einen SQL-Injection-Angriff, bei dem ein cleverer Angreifer einen Wert erstellen kann, der eigentlich mehr SQL ist, wie " MORE SQL HERE " . Es ist eine sehr, sehr, sehr häufige Sicherheitslücke.

Beachten Sie, dass Sie möglicherweise MySQL-eigene verwenden müssen Python-Datenbankbibliothek, um echte vorbereitete Anweisungen zu erhalten . Machen Sie sich darüber keine allzu großen Sorgen, die Verwendung vorbereiteter Anweisungen ist nicht Ihr größtes Leistungsproblem.

Als Nächstes fügen Sie im Grunde eine vorhandene Zeile hinzu oder fügen eine neue Zeile ein, wenn keine Zeile vorhanden ist. Dies kann effizienter in einer einzigen Anweisung mit einem UPSERT erfolgen , ein kombiniertes INSERT und UPDATE . MySQL hat es als INSERT ... ON DUPLICATE KEY UPDATE .

Um zu sehen, wie das gemacht wird, können wir Ihren SELECT then UPDATE schreiben als einzelnes UPDATE . Die Berechnungen erfolgen in 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

Ihr INSERT bleibt gleich...

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

Kombinieren Sie sie zu einem 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)

Dies hängt davon ab, wie die Schlüssel der Tabelle definiert sind. Wenn Sie unique( profile_id, landing_page, keyword ) haben dann sollte es genauso funktionieren wie dein Code.

Auch wenn Sie den Upsert nicht ausführen können, können Sie SELECT eliminieren indem Sie das UPDATE versuchen , prüfen, ob es etwas aktualisiert hat und ob es kein INSERT durchgeführt hat .

Führen Sie die Updates in großen Mengen durch. Anstatt eine Subroutine aufzurufen, die eine Aktualisierung durchführt und festschreibt, übergeben Sie ihr eine große Liste von Dingen, die aktualisiert werden sollen, und bearbeiten Sie sie in einer Schleife. Sie können sogar executemany nutzen dieselbe Anweisung mit mehreren Werten auszuführen. Dann verpflichten Sie sich.

Möglicherweise können Sie den UPSERT ausführen in masse. INSERT kann mehrere Zeilen gleichzeitig aufnehmen. Dies fügt beispielsweise drei Zeilen ein.

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

Sie können wahrscheinlich dasselbe mit Ihrem INSERT ON DUPLICATE KEY UPDATE tun Reduzierung des Aufwands für die Kommunikation mit der Datenbank. Ein Beispiel finden Sie in diesem Beitrag (in PHP, aber Sie sollten sich anpassen können).

Dadurch wird die Rückgabe der ID der zuletzt eingefügten Zeile geopfert, aber das sind die Unterbrechungen.