T-SQL-Concurrency-Problem: Auktions- / Gebotssystem

8

Ich entwickle derzeit ein Online-Auktionssystem unter Verwendung von ASP.NET 3.5 und SQLServer 2008. Ich habe den Punkt in der Entwicklung erreicht, wo ich sicherstellen muss, dass mein System das Concurrency-Problem sinnvoll behandelt, wenn:

Zwei Personen - Geraldine und John - wollen auf dasselbe Auktionsobjekt bieten, das derzeit für 50 £ angeboten wird. Geraldine gibt ein Gebot von £ 55 und John gibt ein Gebot von £ 52. Das System hat jetzt zwei Kopien der Seite 'submit_bid.aspx' ausgeführt; Jede Kopie der Seite prüft, ob ihr Gebot hoch genug ist. Beide sehen, dass es ist, und sie reichen die Gebote ein. Wenn das Gebot von John zuerst durchgeht, beträgt der Auktionspreis derzeit £ 55 und einen Moment später wird es durch ein Gebot von £ 52 ersetzt.

Ich muss die Auktionszeilen sperren, bis der aktuelle Gebotspreis aktualisiert wird, bevor Sie anderen Bietern erlauben, den aktuellen Gebotspreis zu prüfen und ein neues Gebot abzugeben.

Meine Frage ist: Was ist der beste Weg, dies mit T-SQL und / oder ADO.NET zu tun?

Ich habe derzeit eine AuctionItem-Tabelle, die folgende Felder enthält (plus andere Felder, die ich der Kürze halber nicht aufgenommen habe):

%Vor%

Ich habe einige Nachforschungen angestellt und das folgende T-SQL (Pseudocode-ish) gefunden:

%Vor%

Ich habe auch gelesen, dass ich, wenn ich SET LOCK_TIMEOUT einschließe, auch die Anzahl der fehlgeschlagenen gleichzeitigen Aktualisierungen reduzieren kann. Zum Beispiel:

%Vor%

... bewirkt, dass eine gleichzeitige Aktualisierung 1000 Millisekunden lang wartet, bis eine Sperre ausgelöst wird. Ist dies die beste Vorgehensweise?

    
Walter Lockhart 04.07.2011, 15:06
quelle

3 Antworten

0

Ich habe den obigen Vorschlag von Alex K befolgt und eine "Bid History" implementiert. Funktioniert gut. Danke Alex K.

    
Walter Lockhart 03.11.2011, 10:16
quelle
6

Quelle: "chrisrlong", Ссылка

Hier sind die Methoden, die zur Behandlung von Parallelitätsproblemen bei mehreren Benutzern verwendet werden:

  1. Nichts (nicht wünschenswert)

    tun
    • Benutzer 1 liest einen Datensatz
    • Benutzer 2 liest denselben Datensatz
    • Benutzer 1 aktualisiert diesen Datensatz
    • Benutzer 2 aktualisiert denselben Datensatz

    Benutzer 2 hat die Änderungen, die Benutzer 1 vorgenommen hat, jetzt überschrieben. Sie sind komplett weg, als wären sie nie passiert. Dies wird als "verlorenes Update" bezeichnet.

  2. Pessimistisches Sperren (Sperren Sie den Datensatz, wenn er gelesen wird.)

    • Benutzer 1 liest einen Datensatz und sperrt ihn , indem er eine exklusive Sperre auf den Datensatz setzt (FOR UPDATE-Klausel)
    • Benutzer 2 versucht, zu lesen und denselben Datensatz zu sperren, muss aber jetzt hinter Benutzer 1
    • warten
    • Benutzer 1 aktualisiert den Datensatz (und verpflichtet sich natürlich)
    • Benutzer 2 kann jetzt den Datensatz mit den Änderungen lesen, die Benutzer 1 gemacht hat
    • Benutzer 2 aktualisiert den Datensatz mit den Änderungen von Benutzer 1

    Das Problem mit dem verlorenen Update ist gelöst. Das Problem bei diesem Ansatz ist Nebenläufigkeit. Benutzer 1 sperrt einen Datensatz, den sie möglicherweise nie aktualisieren. Benutzer 2 kann den Datensatz nicht einmal lesen, weil er beim Lesen auch eine exklusive Sperre wünscht. Dieser Ansatz erfordert viel zu viel exklusives Sperren, und die Sperren leben viel zu lange (oft über Benutzerkontrolle - ein absolut no-no). Dieser Ansatz ist fast nie implementiert.

  3. Optimistische Sperre verwenden.
    Optimistisches Sperren verwendet beim Lesen keine exklusiven Sperren. Stattdessen wird während des Updates eine Überprüfung durchgeführt, um sicherzustellen, dass der Datensatz seit dem Lesen nicht geändert wurde. Im Allgemeinen wird dies durch Hinzufügen einer Version / etc-Spalte (INT / numerisch, die einen numerischen Wert enthält, der erhöht wird, wenn eine UPDATE-Anweisung erstellt wird) durchgeführt. IE:

    %Vor%

    Eine alternative Option ist die Verwendung eines Zeitstempels anstelle einer numerischen Spalte. Diese Spalte wird für keinen anderen Zweck verwendet als die Implementierung optimistischer Parallelität. Es kann eine Nummer oder ein Datum sein. Die Idee ist, dass es einen Wert erhält, wenn die Zeile eingefügt wird. Immer wenn der Datensatz gelesen wird, wird auch die Timestamp-Spalte gelesen. Wenn eine Aktualisierung durchgeführt wird, wird die Zeitstempelspalte überprüft. Wenn es zur UPDATE-Zeit denselben Wert hat wie beim Lesen, dann ist alles in Ordnung, das UPDATE wird ausgeführt und der Timestamp wird geändert! . Wenn der Zeitstempelwert zur UPDATE-Zeit unterschiedlich ist, wird ein Fehler an den Benutzer zurückgegeben - er muss den Datensatz erneut lesen, die Änderungen erneut vornehmen und versuchen, den Datensatz erneut zu aktualisieren.

    • Benutzer 1 liest den Datensatz einschließlich des Zeitstempels von 21
    • Benutzer 2 liest den Datensatz einschließlich des Zeitstempels von 21
    • Benutzer 1 versucht, den Datensatz zu aktualisieren. Der Zeitstempel in had (21) stimmt mit dem Zeitstempel in der Datenbank überein (21), sodass die Aktualisierung durchgeführt wird und der Zeitstempel aktualisiert wird (22).
    • Benutzer 2 versucht, den Datensatz zu aktualisieren. Der Zeitstempel (21) entspricht nicht dem Zeitstempel in der Datenbank (22), daher wird ein Fehler zurückgegeben. Benutzer 2 muss nun den Datensatz erneut lesen, einschließlich des neuen Zeitstempels (22) und der Änderungen von Benutzer 1, die Änderungen erneut anwenden und das Update erneut versuchen.

Vergleich

  • Optimistisches Sperren ist datenbankunabhängig - es müssen keine Isolationsstufen und keine datenbankspezifische Syntax für Isolationsstufen verwendet werden.
  • Ich würde eine numerische Spalte über einen Zeitstempel verwenden - weniger Daten & amp; Ärger zu verwalten
OMG Ponies 04.07.2011 16:13
quelle
3

Sie brauchen keine Transaktion, wenn Sie nur 1 Anweisung wie folgt verwenden:

%Vor%     
t-clausen.dk 04.07.2011 15:16
quelle