Heim > Java > javaLernprogramm > Hauptteil

Wie man mit Race Conditions mit Java und PostgreSQL umgeht

WBOY
Freigeben: 2024-07-18 10:15:30
Original
976 Leute haben es durchsucht

How to deal with race conditions using Java and PostgreSQL

Verwenden von Sperren zur Steuerung der Datenbank-Parallelität

Stellen Sie sich vor, Sie arbeiten an einem E-Commerce-System und Tausende von Menschen versuchen gleichzeitig, das letzte verbleibende Produkt zu kaufen. Viele von ihnen könnten jedoch zur Kasse gehen und die Bestellung abschließen. Wenn Sie Ihren Lagerbestand überprüfen, haben Sie ein Produkt mit einer negativen Menge. Wie war das möglich und wie können Sie das Problem lösen?

Lass uns programmieren! Das erste, woran Sie vielleicht denken, ist, den Lagerbestand vor dem Bezahlen zu überprüfen. Vielleicht so etwas:

public void validateAndDecreaseSolution(long productId, int quantity {
    Optional<StockEntity> stockByProductId = 
 stockRepository.findStockByProductId(productId);

    int stock = stockByProductId.orElseThrow().getStock();
    int possibleStock = stock - quantity;

    if (stock <= 0 || possibleStock < 0) {
        throw new OutOfStockException("Out of stock");
    }

    stockRepository.decreaseStock(productId, quantity);
}
Nach dem Login kopieren

Sie können diese Validierung verwenden, aber wenn wir von Hunderten, Tausenden, Millionen oder sogar Dutzenden von Anfragen pro Sekunde sprechen, wird diese Validierung nicht ausreichen. Wenn 10 Anfragen genau zur gleichen Zeit diesen Codeabschnitt erreichen und die Datenbank denselben Wert für stockByProductId zurückgibt, bricht Ihr Code zusammen. Sie benötigen eine Möglichkeit, andere Anfragen zu blockieren, während wir diese Überprüfung durchführen.

Erste Lösung - FÜR UPDATE

Fügen Sie eine Sperranweisung zu Ihrem SELECT hinzu. In diesem Beispiel habe ich dies mit FOR UPDATE mit Spring Data gemacht. Wie es in der PostgreSQL-Dokumentation heißt

FOR UPDATE bewirkt, dass die von der SELECT-Anweisung abgerufenen Zeilen wie für eine Aktualisierung gesperrt werden. Dadurch wird verhindert, dass sie durch andere Transaktionen geändert oder gelöscht werden, bis die aktuelle Transaktion endet.

@Query(value = "SELECT * FROM stocks s WHERE s.product_id = ?1 FOR UPDATE", nativeQuery = true)
Optional<StockEntity> findStockByProductIdWithLock(Long productId);
Nach dem Login kopieren
public void validateAndDecreaseSolution1(long productId, int quantity) {
    Optional<StockEntity> stockByProductId = stockRepository.findStockByProductIdWithLock(productId);

    // ... validate

    stockRepository.decreaseStock(productId, quantity);
}
Nach dem Login kopieren

Alle Anfragen an die Lagerbestandstabelle unter Verwendung der Produkt-ID warten, bis die tatsächliche Transaktion abgeschlossen ist. Ziel ist es, sicherzustellen, dass Sie den letzten aktualisierten Wert der Aktie erhalten.

Zweite Lösung – pg_advisory_xact_lock

Diese Lösung ähnelt der vorherigen, Sie können jedoch auswählen, was der Sperrschlüssel ist. Wir sperren die gesamte Transaktion, bis wir die gesamte Verarbeitung der Validierung und Bestandsreduzierung abgeschlossen haben.

public void acquireLockAndDecreaseSolution2(long productId, int quantity) {
    Query nativeQuery = entityManager.createNativeQuery("select pg_advisory_xact_lock(:lockId)");
    nativeQuery.setParameter("lockId", productId);
    nativeQuery.getSingleResult();

    Optional<StockEntity> stockByProductId = stockRepository.findStockByProductId(productId);

    // check stock and throws exception if it is necessary

    stockRepository.decreaseStock(productId, quantity);
}
Nach dem Login kopieren

Die nächste Anfrage interagiert erst mit einem Produkt mit derselben ID, nachdem diese Transaktion beendet ist.

Dritte Lösung – WHERE-Klausel

In diesem Fall sperren wir unsere Zeile oder Transaktion nicht. Lassen wir zu, dass diese Transaktion bis zur Aktualisierungsanweisung fortgesetzt wird. Beachten Sie die letzte Bedingung: Lagerbestand > 0. Dies wird nicht zulassen, dass unser Lagerbestand kleiner als Null ist. Wenn also zwei Personen gleichzeitig versuchen zu kaufen, erhält einer von ihnen eine Fehlermeldung, da unsere Datenbank keinen Lagerbestand <= -1.
zulässt

@Transactional
@Modifying
@Query(nativeQuery = true, value = "UPDATE stocks SET stock = stock - :quantity WHERE product_id = :productId AND stock > 0")
int decreaseStockWhereQuantityGreaterThanZero(@Param("productId") Long productId, @Param("quantity") int quantity);




Abschluss

Die erste und zweite Lösung verwenden pessimistisches Sperren als Strategie. Das dritte ist das optimistische Sperren. Die pessimistische Sperrstrategie wird verwendet, wenn Sie einen restriktiven Zugriff auf eine Ressource wünschen, während Sie eine Aufgabe ausführen, die diese Ressource betrifft. Die Zielressource wird für jeden anderen Zugriff gesperrt, bis Sie Ihren Vorgang abschließen. Seien Sie vorsichtig mit Deadlocks!

Mit optimistischer Sperrung können Sie verschiedene Abfragen für dieselbe Ressource ohne Blockierung durchführen. Es wird verwendet, wenn Konflikte unwahrscheinlich sind. Normalerweise verfügen Sie über eine Version, die sich auf Ihre Zeile bezieht, und wenn Sie diese Zeile aktualisieren, vergleicht die Datenbank Ihre Zeilenversion mit der Zeilenversion in der Datenbank. Wenn beide gleich sind, ist die Änderung erfolgreich. Wenn nicht, müssen Sie es erneut versuchen. Wie Sie sehen, verwende ich in diesem Artikel keine Versionszeile, aber meine dritte Lösung blockiert keine Anfragen und steuert die Parallelität mithilfe des Bestands > 0 Zustand.

Wenn Sie den vollständigen Code sehen möchten, können Sie auf meinem GitHub nachsehen.

Es gibt viele andere Strategien, um pessimistisches und optimistisches Sperren zu implementieren. Sie können beispielsweise mehr nach FOR UPDATE WITH SKIP LOCKED suchen.

Das obige ist der detaillierte Inhalt vonWie man mit Race Conditions mit Java und PostgreSQL umgeht. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Quelle:dev.to
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage
Über uns Haftungsausschluss Sitemap
Chinesische PHP-Website:Online-PHP-Schulung für das Gemeinwohl,Helfen Sie PHP-Lernenden, sich schnell weiterzuentwickeln!