Heim  >  Artikel  >  Datenbank  >  Welche beiden Fallstricke müssen bei verteilten Redis-Sperren vermieden werden?

Welche beiden Fallstricke müssen bei verteilten Redis-Sperren vermieden werden?

WBOY
WBOYnach vorne
2023-05-29 22:52:041221Durchsuche

1 Der erste Fallstrick: Falscher Zeitpunkt der Sperrenfreigabe

1.1. Entdecken Sie das Problem

Analysieren Sie die Probleme im folgenden Code:

// 分布式锁服务
public interface RedisLockService {
    // 获取锁
    public boolean getLock(String key);
    // 释放锁
    public boolean releaseLock(String key);
}

// 业务服务
public class BizService {

    @Resource
    private RedisLockService redisLockService;

    public void bizMethod(String bizId) {
        try {
            // 获取锁
            if(redisLockService.getLock(bizId)) {
                // 业务重复校验
                if(!bizValidate(bizId)) {
                    throw new BizException(ErrorBizCode.REPEATED);
                }
                // 执行业务
                return doBusiness();
            }
            // 获取锁失败
            throw new BizException(ErrorBizCode.GET_LOCK_ERROR);
        } finally {
            // 释放锁
            redisLockService.releaseLock(bizId);
        }
    }
}

Der obige Code scheint in Ordnung zu sein, aber tatsächlich verbirgt er ein großes Problem. Das Problem besteht darin, dass beim Aufheben der Sperre nicht überprüft wird, ob der aktuelle Thread die Sperre erhalten hat:

  • Thread 1 und Thread 2 greifen gleichzeitig auf die Geschäftsmethode zu

  • Thread 2 erhält die Sperre erfolgreich und führt die Geschäftsverarbeitung aus

  • Thread 1 erhält sie nicht. Die Sperre wird erhalten, aber die Sperre wird erfolgreich aufgehoben.

  • Zu diesem Zeitpunkt versucht Thread 3, die Sperre erfolgreich zu erhalten, aber das Geschäft von Thread 2 wurde nicht ausgeführt verarbeitet, sodass Thread 3 keine Geschäftsduplikationsausnahme auslöst

  • Letztendlich werden Thread 2 und Thread 3 wiederholt. Geschäft ausführen

1.2 Lösung des Problems

Die Lösung besteht darin, die Freigabe der Sperre erst danach zuzulassen Bestätigung, dass die Sperrenerfassung erfolgreich war:

public class BizService {

    @Resource
    private RedisLockService redisLockService;

    public void bizMethod(String bizId) {
        boolean getLockSuccess = false;
        try {
            // 尝试获取锁
            getLockSuccess = redisLockService.getLock(bizId);
            // 获取锁成功
            if(getLockSuccess) {
                // 业务重复校验
                if(!bizValidate(bizId)) {
                    throw new BizException(ErrorBizCode.REPEATED);
                }
                // 执行业务
                return doBusiness();
            }
            // 获取锁失败
            throw new BizException(ErrorBizCode.GET_LOCK_ERROR);
        } finally {
            // 获取锁成功才允许释放锁
            if(getLockSuccess) {
                redisLockService.releaseLock(bizId);
            }
        }
    }
}

2 Die zweite Grube: Cache-Ungültigmachungsproblem

Das zweite Problem besteht darin, dass Redis auch über einen Speicherbereinigungsmechanismus verfügt, der dazu führen kann, dass die verteilte Sperre fehlschlägt. 2.1 Mechanismus zur Bereinigung des Ablaufs Bei vielen Schlüsseln kostet das regelmäßige Löschen sehr viel Ressourcen, daher wird eine Strategie zum verzögerten Löschen eingeführt

Wenn Redis beim Zugriff feststellt, dass der Schlüssel abgelaufen ist, wird er direkt gelöscht

2.2 SpeicherrecyclingmechanismusWenn nicht genügend Speicher vorhanden ist , Redis wählt einige Elemente zum Löschen aus:

no-enviction

Räumung von Daten verbieten, neue Schreibvorgänge melden einen Fehlervolatile-lru

Wählen Sie die zuletzt verwendeten Daten aus dem Datensatz aus, wobei die Ablaufzeit auf eingestellt ist Beseitigen Sie es

volatile-ttl

Wählen Sie die Daten aus, die aus dem Datensatz ablaufen sollen, mit festgelegter Ablaufzeit

volatile-random

Wählen Sie beliebige Daten aus dem Datensatz zur Eliminierung aus

allkeys-lru

Wählen Sie die zuletzt verwendeten Daten aus Daten aus dem Datensatz zur Eliminierung

allkeys-random

Wählen Sie beliebige Daten aus dem Datensatz aus. Dateneliminierung

Es gibt mindestens zwei Szenarien, die zu einem verteilten Sperrfehler führen:

Szenario 1: Redis hat nicht genügend Speicher für die Speicherwiederverwendung. und die Verwendung der

Recycling-Strategie führt zu einem Sperrfehler

Szenario 2: Der Thread erhält die verteilte Sperre erfolgreich, aber die Geschäftsverarbeitungszeit ist zu lang. Zu diesem Zeitpunkt läuft die Sperre ab und wird regelmäßig gelöscht, was dazu führt, dass andere Threads erfolgreich erworben werden Öffnen Sie die Sperre und führen Sie das Geschäft wiederholt aus

2.3 Optimistische Sperre

Die allgemeine Lösung besteht darin, sie auf der Datenbankebene zu schützen, z. B. beim Bestandsabzugsgeschäft in Die Datenbankebene verwendet optimistische Sperren.
    udpate goods set stock = stock - #{acquire} 
    where sku_id = #{skuId} and stock - #{acquire} >= 0

Das obige ist der detaillierte Inhalt vonWelche beiden Fallstricke müssen bei verteilten Redis-Sperren vermieden werden?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:yisu.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen