Maison  >  Article  >  base de données  >  Quels sont les deux écueils que les verrous distribués Redis doivent éviter ?

Quels sont les deux écueils que les verrous distribués Redis doivent éviter ?

WBOY
WBOYavant
2023-05-29 22:52:041262parcourir

1 Le premier piège : Mauvais timing de déverrouillage

1.1. Découvrez le problème

Analysez les problèmes dans le code suivant :

// 分布式锁服务
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);
        }
    }
}

Le code ci-dessus semble bien, mais en fait il cache un gros problème. Le problème est que lors de la libération du verrou, il n'y a aucune vérification si le thread actuel a obtenu le verrou :

  • Le thread 1 et le thread 2 accèdent à la méthode métier en même temps

  • Le thread 2 acquiert avec succès le verrou et effectue le traitement commercial

  • Le thread 1 ne l'a pas acquis Le verrou est obtenu, mais le verrou est libéré avec succès

  • À ce moment, le thread 3 tente d'acquérir le verrou avec succès, mais l'activité du thread 2 n'a pas été traité, donc le thread 3 ne provoquera pas d'exception de duplication d'activité

  • En fin de compte, le thread 2 et le thread 3 sont répétés Exécution de l'activité

1.2 Résolution du problème

La solution est d'autoriser la libération du verrou uniquement après confirmation que l'acquisition du verrou est réussie :

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 Le deuxième problème : problème d'invalidation du cache

Le deuxième problème est que Redis dispose également d'un mécanisme de nettoyage de la mémoire , ce qui peut provoquer l'échec du verrou distribué.

2.1 Mécanisme de nettoyage après expiration

(1) Suppression régulière

Redis vérifie régulièrement quelles clés ont expiré et les supprime si elles s'avèrent expirées

(2) Suppression paresseuse

S'il y en a trop de nombreuses clés, la suppression régulière coûtera très cher en ressources, donc une stratégie de suppression paresseuse est introduite

Si Redis constate que la clé a expiré lors de l'accès, elle sera supprimée directement

2.2 Mécanisme de recyclage de la mémoire

Lorsque la mémoire est insuffisante , Redis sélectionnera certains éléments à supprimer :

no-enviction

Interdire l'expulsion des données, les nouvelles opérations d'écriture signaleront une erreur

volatile-lru

Sélectionnez les données les moins récemment utilisées dans l'ensemble de données avec le délai d'expiration défini sur éliminez-le

volatile-ttl

Sélectionnez les données à expirer dans l'ensemble de données avec un délai d'expiration défini pour les éliminer

volatile-random

Choisissez des données arbitraires dans l'ensemble de données à éliminer

allkeys-lru

Sélectionnez le données les moins récemment utilisées de l'ensemble de données pour l'élimination

allkeys-random

Choisissez arbitrairement dans l'ensemble de données Élimination des données

Il existe au moins deux scénarios qui conduisent à un échec du verrouillage distribué :

  • Scénario 1 : Redis n'a pas de mémoire suffisante pour le recyclage de la mémoire et l'utilisation de la allkeys-lru或者allkeys-randomstratégie de recyclage provoque un échec du verrouillage

  • Scénario 2 : le thread acquiert le verrou distribué avec succès, mais le temps de traitement métier est trop long. À ce moment, le verrou expire et est effacé régulièrement, provoquant d'autres threads. pour acquérir avec succès le verrou et exécuter l'entreprise à plusieurs reprises

2.3 Verrouillage optimiste

La solution générale consiste à le protéger au niveau de la couche de base de données, comme les activités de déduction des stocks dans La couche de base de données utilise un verrouillage optimiste.

udpate goods set stock = stock - #{acquire} 
where sku_id = #{skuId} and stock - #{acquire} >= 0

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer