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
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); } } } }
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
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
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 SperrfehlerSzenario 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 aus2.3 Optimistische SperreDie 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!