dr.colossos: parallele INSERTS

Hi,

folgendes Skript wird von mindestens zwei PHP-Prozessen (A, B) parallel ausgefuehrt.

[...]
$conn->begin_transaction();
for($i = 0; $i < 5; $i++)
{
 $max_id = $conn->db_get_value('select max(primary_id) + 1 AS max_id from tabelle');
 $conn->db_access('insert into tabelle (primary_id) VALUES ('.$max_id.')');
}
$conn->end_transaction();
[...]

Es startet jeweils eine Transaktion, ermittelt die aktuell hoechste ID der Tabelle und addiert 1, und schreibt einen neuen Satz mit dieser neuen hoechten ID, und wiederholt das (hier testweise insgesamt 5 mal in der for-Schleife), und beendet die Transaktion.

Dadurch, dass dieses Skript parallel von mehrerer Prozessen ausgefuehrt wird, kann es sein, dass Prozess A beim 1. Durchlauf der Schleife einen neuen Datensatz mit ID Y anlegen will, und dadurch, das Prozess B parallel laeuft, auch dieser einen Datensatz mit ID Y anlegen will, da er die max-id ermittelt hat, bevor (!) Prozess A das INSERT ausgefuehrt hat.

Ich habe das einige Male getestet, in der Regel funktioniert es, da, sobald ein INSERT begonnen wurde, der andere Prozess, der ja auch in einer Transaktion liegt wartet, bis die andere INSERT-Batch-Transaktion fertig ist.

Allerdings hat es auch ein paar mal aus oben genannten Grund gekracht, da die SELECTS (fast) gleichzeitig fertig waren und vorallem BEVOR einer der INSERTS abgesetzt wurde.

Wie kann man das 100% umgehen?

Trigger, Auto-ID, Locks?

Danke & Servus

  1. Hallo

    for($i = 0; $i < 5; $i++)
    {
    $max_id = $conn->db_get_value('select max(primary_id) + 1 AS max_id from tabelle');
    $conn->db_access('insert into tabelle (primary_id) VALUES ('.$max_id.')');
    }

    Aua! Das macht man nicht. Verwende den Mechanismus, den Dir Dein DBMS oder
    Dein DB-Abstraktionslayer bietet.

    Auto-ID

    Sequenzen, Identity-Werte, ... wie auch immer. Ich weiß ja nicht, was Deine
    Zielplattform unterstützt.

    Freundliche Grüße

    Vinzenz

    1. Hi,

      und danke fuer deine Antwort.

      "Ich weiß ja nicht, was Deine Zielplattform unterstützt."

      Ja, ich will's halt auf "allen" laufen lassen koennen. Dass das keine gute Loesung ist weiss ich, ich habe nur auf was allgemeines gehofft, das ueberall funktioniert, ohne abstahieren zu muessen.

      Auto-ID ist inzwischen verbreitet (MSSQL, PGSQL koennen das, glaub ich), aber ORACLE macht das glaub ich nur via Trigger ...

      Hat noch jemand eine Idee?

      Danke

      1. Hallo

        "Ich weiß ja nicht, was Deine Zielplattform unterstützt."

        Ja, ich will's halt auf "allen" laufen lassen koennen. Dass das keine gute Loesung ist weiss ich, ich habe nur auf was allgemeines gehofft, das ueberall funktioniert, ohne abstahieren zu muessen.

        "alle" gibt es nicht, das weißt Du doch sicher auch. Ich kann mir nicht vorstellen, dass Du etwa noch dBase unterstützen willst.

        Welche Zielplattformen kommen für Dich denn in Frage?

        Auto-ID ist inzwischen verbreitet (MSSQL, PGSQL koennen das, glaub ich), aber ORACLE macht das glaub ich nur via Trigger ...

        ORACLE stellt meines Wissens Sequenzen zur Verfügung, vergleichbar zu SERIAL/BIGSERIAL von PostgreSQL.

        Was machen übrigens Deine netten beginTransaction()-, endTransaction()-Aufrufe,
        wenn das DBMS Transaktionen gar nicht unterstützt?

        Freundliche Grüße

        Vinzenz

        1. Hehe,

          jaja, das "alles" war natuerlich Schrott, schon klar .. waer mir am liebsten, aber das ist eine Illusion.

          Was auch noch eine Moeglichkeit waere ist GUIDs/UUIDs als IDs zu verwenden.

          Ja, ich denke man kann da auch einige Besucher hier entweder weiss oder rot im Gesicht anlaufen sehen, aber ich stell das trotzdem mal zur Diskussion.

          Das ist dann je nach "Sicherheit" des Zufallsgenerators was zwischen microtime, PHPs uniqid() und dessen Abwandlungen, bis hin zur Simulations von GUIDs [2c6973e6-828d-4ddf-afc9-2eb2d308de8d].

          Vorteile:

          • DB uebergreifend
          • vor und nach INSERT bekannt (muss bei AUTO-ID evtl "kompliziert" ermittelt werden)
          • aenderbar (was wohl nicht noetig sein sollte, aber wer weiss ...)
          • ... ?

          Nachteil:

          • Performance ?
          • nicht sortierbar (was wohl auch nicht noetig sein sollte, aber wer weiss ...)

          Meinungen, Wutausbrueche oder Schreikraempfe die jemand loswerden will?

          Danke

      2. echo $begrüßung;

        Ja, ich will's halt auf "allen" laufen lassen koennen. Dass das keine gute Loesung ist weiss ich, ich habe nur auf was allgemeines gehofft, das ueberall funktioniert, ohne abstahieren zu muessen.

        Es gibt genügend Datenbankabstraktionen unter PHP.

        Sequenzen kann man simulieren, man braucht dazu aber etwas Locking und eine extra Tabelle. Jede Sequenz bekommt darin ihren eigenen Datensatz. Sperren, Wert auslesen, Wert erhöhen, Freigeben.

        echo "$verabschiedung $name";