[Idempotency Pit] Das Aufheben der Sperre vor dem Festschreiben der Transaktion führt dazu, dass die Sperre fehlschlägt

Navigation:

[Java-Notizen + Stepping on the Pit-Zusammenfassung] Java-Grundlagen + Fortgeschrittene + JavaWeb + SSM + SpringBoot + St. Regis Takeaway + SpringCloud + Dark Horse Tourism + Guli Mall + Xuecheng Online + MySQL-Erweitertes Kapitel + Designmodus + Häufige Interviewfragen + Quelle Code

Inhaltsverzeichnis

1. Problemanalyse

1.1 Das Versagen der Idempotenz führt zur wiederholten Einreichung von Formularen

1.2 Das Problem des Überverkaufs in Sekunden

Zweitens, die Lösung

2.1 Lösung 1: Fügen Sie einen eindeutigen Index hinzu

2.2 Lösung 2: Sperren Sie die äußere Schicht der Transaktion

2.3 Lösung drei: Verschachtelte Transaktionen


1. Problemanalyse

1.1 Das Versagen der Idempotenz führt zur wiederholten Einreichung von Formularen

Problem: Bei hoher Parallelität ist die Systemlast hoch. Wenn verteilte Sperren verwendet werden, um Idempotenz zu erreichen, erwerben andere Threads die Sperre und übermitteln die Transaktion während des Zeitraums vom Entsperren bis zum Festschreiben der Transaktion.

Ergebnis: Zwei Teile derselben Daten wurden vom selben Benutzer eingefügt.

Pseudocode-Simulation:

@Transactional

public void submit() {

    boolean lockFlag=lock();    //例如setnx

    if(!lockFlag) {
//加锁失败

        throw new RuntimeException(“请稍后再试”);

    }

//加锁成功
    if(!dao.query())    //如果查不到数据
    dao.insert();    //则插入一条数据

    unlock();

//解锁后,事务还没来得及提交,此线程就阻塞了。
//此时另一个线程成功获取、查数据(数据依然不存在,因为上个线程的事务并没有提交)插数据、释放锁、提交事务。

}
//此线程执行完毕,又提交了一次事务。导致两个线程都成功插入了数据。

In der Transaktion wird die verteilte Redis-Sperre verwendet. Sobald diese Methode ausgeführt wird, wird die Transaktion wirksam, und dann wird die verteilte Redis-Sperre wirksam. Nachdem der Code ausgeführt wurde, wird zuerst die verteilte Redis-Sperre und dann die Transaktionsdaten freigegeben wird übermittelt und schließlich endet die Transaktion. In diesem Prozess wurde die verteilte Sperre freigegeben, bevor die Transaktion festgeschrieben wurde,  was zur Ungültigkeit der verteilten Sperre führte.

1.2 Das Problem des Überverkaufs in Sekunden

Fall Nummer eins:

加锁{
    查表
    取值
    更新
}
释放锁
 

Nehmen Sie als Beispiel die Threads A und B:

  1. Thread A erhält die Sperre,
  2. Thread A überprüft die Benutzertabelle, um den Kontostand zu erhalten.
  3. Faden A plus Rest vom vorderen Ende,
  4. Thread A aktualisiert die Datenbank.
    • offene Transaktion
    • Führen Sie die Aktualisierungsanweisung aus ( beachten Sie, dass das Programm zu diesem Zeitpunkt nacheinander ausgeführt wird, um die Sperre aufzuheben, und Thread B die Sperre erhält. )
      • Thread B erhält die Sperre,
      • Fragen Sie die Benutzertabelle ab, um den Kontostand vor der Aktualisierung zu erhalten .
    • Transaktion festschreiben
  5. Faden B plus Rest vom vorderen Ende,
  6. Thread B aktualisiert die Datenbank.

Fall zwei:

@Transactional

public void seckill() {

    boolean lockFlag=lock();    //例如setnx

    if(!lockFlag) {
//加锁失败

        throw new RuntimeException(“请稍后再试”);

    }

//加锁成功
    if(dao.queryStock()>0){    //如果查询库存有余额。例如余额是1
        dao.updateStock();    //则减库存。此时余额是0
    }

    unlock();

    //此时解锁成功了,因为事务还没有提交,此线程又阻塞了,此时另一个线程成功获取释放锁、查询库存是1(因为读不到未提交事务的数据),就减库存提交事务。

}
//此线程执行完毕,因为没有异常,所以又提交了一次事务,导致多卖了一次商品

Zweitens, die Lösung

2.1 Lösung 1: Fügen Sie einen eindeutigen Index hinzu

Wenn das Formular wiederholt gesendet wird, können Sie versuchen, einem eindeutigen Feld wie „Bestellnummer“ einen eindeutigen Index hinzuzufügen, sodass der Index bei wiederholter Übermittlung aufgrund der eindeutigen Indexbeschränkung ungültig wird.

Verwenden Sie den Parameter UNIQUE, um den Index als eindeutigen Index festzulegen. Beim Erstellen eines eindeutigen Index muss der Wert des Index eindeutig sein , es sind jedoch mehrere Nullwerte zulässig . Eine Datentabelle kann mehrere eindeutige Indizes enthalten.

Der Unterschied zwischen eindeutigen Einschränkungen und eindeutigen Indizes:
1. Sowohl eindeutige Einschränkungen als auch eindeutige Indizes können die Einzigartigkeit von Spaltendaten realisieren, und Spaltenwerte können Null sein.
2. Durch das Erstellen einer eindeutigen Einschränkung wird automatisch ein eindeutiger Index mit demselben Namen erstellt. Dieser Index kann nicht einzeln gelöscht werden. Durch das Löschen der Einschränkung wird der Index automatisch gelöscht. Die eindeutige Einschränkung besteht darin, die Einzigartigkeit der Daten durch den eindeutigen Index zu realisieren .
3. Erstellen Sie einen eindeutigen Index, der unabhängig ist und separat gelöscht werden kann.
4. Wenn Sie Einschränkungen und Indizes für eine Spalte haben möchten, können beide separat gelöscht werden. Sie können zuerst einen eindeutigen Index und dann eine eindeutige Einschränkung mit demselben Namen erstellen.
5. Wenn ein Feld einer Tabelle als Fremdschlüssel einer anderen Tabelle verwendet werden soll, muss dieses Feld eine eindeutige Einschränkung (oder einen Primärschlüssel) haben. Wenn nur ein eindeutiger Index vorhanden ist, wird ein Fehler gemeldet.

2.2 Lösung 2: Sperren Sie die äußere Schicht der Transaktion

  • Verteilte Sperren werden in der Controller-Schicht und Transaktionen in der Service-Schicht hinzugefügt.
  • Bei programmgesteuerten Transaktionen ist die äußere Schicht eine Sperre und die innere Schicht eine Transaktion.

2.3 Lösung drei: Verschachtelte Transaktionen

Kapseln Sie den Vorgang des Nachschlagens der Tabelle und der separaten Aktualisierung der Tabelle in einer Methode ( Sperren außerhalb der Transaktion ). Fügen Sie dann eine Frühlingstransaktion hinzu ( verschachteltes Commit ).

@Transactional
public void submit() {
    if(lock()){
//提交表单的业务逻辑会生成一个嵌套事务,子事务提交回滚独立于外层事务。
        xxxService.submitAfterUnLock();    //注意别用this调用,会失效。
    }
    unlock();
    
    //此时另一个线程,
}

@Transactional(propagation = Propagation.NESTED)    //嵌套事务
public void submitAfterUnLock() {
    if(!dao.query()) insert();//如果查不到表单(通过订单号),则提交表单。

}

Acho que você gosta

Origin blog.csdn.net/qq_40991313/article/details/131502762
Recomendado
Clasificación