Redis分布式锁

本文介绍了如何使用Redis实现分布式锁,分为悲观锁和乐观锁两种方式。悲观锁利用SET命令的NX和EX参数确保原子操作,防止死锁。乐观锁则通过WATCH、MULTI和EXEC事务操作,当并发修改时,失败的消费者可以重试直至成功。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、悲观锁

使用redis实现分布式悲观锁,主要是使用了redis原生命令的set命令(http://redisdoc.com/string/set.html),

SET key value [EX seconds] [PX milliseconds] [NX|XX]

从 Redis 2.6.12 版本开始, SET 命令的行为可以通过一系列参数来修改,比如NX参数,就能取代SETNX命令。【EX seconds】参数又将SET命令和EXPIRE命令结合为原子操作,这就为我们的分布式悲观锁打下了基础。

假设A、B分别是分布式系统同个资源的消费者,在获取该资源前,需要获取锁,才能对资源进行操作(否则会引起冲突)。此时A、B同时向redis发起命令:

//mykey代表key值,myValue代表value值,EX 10代表10秒失效,NX代表不存在该key才进行操作,否则返回nil
set myKey myValue EX 10 NX

有且只有一个消费者能set成功(获得OK),其余的都会返回nil。

该锁的好处在于,如果A消费者获取锁成功后,即使A挂了,锁也会在规定的失效时间内解锁,不会造成死锁问题。

Java代码的实现如下(此处使用了redisTemplate的连接工具):

public String setRedis(String key, String value) {
        Boolean execute = redisTemplate.execute(new RedisCallback<Boolean>() {

            @Override
            public Boolean doInRedis(RedisConnection redisConnection) throws DataAccessException {
                return redisConnection.set(key.getBytes(), value.getBytes(), Expiration.from(Duration.ofSeconds(100)), RedisStringCommands.SetOption.ifAbsent());
            }
        });
        if (execute)
            return "OK";
        else
            return "Failed";
    }

二、乐观锁

redis实现乐观锁,主要是使用了redis的如下事务的命令:

1.multi,开启Redis的事务,置客户端为事务态。
2.exec,提交事务,执行从multi到此命令前的命令队列,置客户端为非事务态。
3.discard,取消事务,置客户端为非事务态。
4.watch,监视键值对,作用时如果事务提交exec时发现监视的监视对发生变化,事务将被取消。

消费者A、B需要对变量v进行修改,他们同时进行如下操作:观察(watch命令)v,开启事务(multi),执行修改v的操作,提交事务(exec),中途如果有其他消费者已经修改了变量v,那么根据redis的事务的特性,exec提交的结果就是不成功。此时不成功的消费者可以继续重试,直至成功或者达到最大重试次数。

以下是java代码的实现方式:

public Object positiveLock(String key, String value) throws Exception {
        int maxTryTime = 100;
        while(true) {
            //execute返回事务内多个命令的执行结果
            List execute = (List)redisTemplate.execute(new SessionCallback<Object>() {
                @Override
                public Object execute(RedisOperations operations) throws DataAccessException {
                    operations.watch(key);
                    operations.multi();
                    operations.delete("lalall");
                    operations.opsForValue().set(key, value);
                    operations.opsForValue().set("abcdefg", value);
                    Object val = operations.exec();
                    return val;
                }
            });
            if(execute == null){
                throw new Exception("未知异常");
            }
            //乐观锁获取成功
            if(execute != null && execute.size() > 0){
                break;
            }
            //重试次数太多
            if(maxTryTime-- < 0){
                break;
            }
        }

        return "OK";
    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值