【Redis】Redis分布式锁探索入门

入坑1(过期时间):

锁没有设置过期时间,程序报错,该锁没有删除,导致锁一致存在,其他线程永远无法获取锁
错误方式

def redis_lock
	lock_key = 'redis:nx:key'
	lock_val = UUID
	-- 设置锁
	redis.setnx(lock_key, lock_val)
	-- ##########
	-- 此时这里报错,,,该锁无法删除!!!
	-- ##########
	redis.del(lock_key)
	
end

解决

def redis_lock
	lock_key = 'redis:nx:key'
	lock_val = UUID
	-- 设置锁
	redis.setnx(lock_key, lock_val)
	-- 设置过期时间
	redis.expire(lock_key, 30)
	-- ##########
	-- 此时这里报错,,,但是设置过期时间,30s后会自动删除锁
	-- ##########
	redis.del(lock_key)
end

入坑2(加锁原子性):

设置锁的操作不是原子性,导致锁没有设置上上过期时间
伪代码如下:
错误方式

def redis_lock
	lock_key = 'redis:nx:key'
	lock_val = UUID
	-- 设置锁
	redis.setnx(lock_key, lock_val)
	-- ##########
	-- 此时可能因为各种问题没有执行到这里,,,没有设置上过期时间,,,也没有执行到删除key操作,,,这个锁其他线程无法删除!!!
	-- ##########
	redis.expire(lock_key, 30)
	redis.del(lock_key)
end

解决

-- 使用官方推荐的lua脚本,将加锁操作原子化,就不会导致如上的情况出现
def redis_lock
	lock_key = 'redis:nx:key'
	lock_val = UUID
	redis.eval("if redis.call('set', KEY[1], VAL[1], 'EX', '30', 'NX') then return 1; else return 0; end;", [lock_key], [lock_val])
	redis.del(lock_key)
end

入坑3(删除其他线程锁):

线程A拿到锁,开始去获取数据,但是这时候,可能由于网络原因导致业务代码长时间没有执行完,锁时间过期,此时线程B来了获取到锁,此时线程A业务执行完成,,,啪一下把线程B的锁直接删了,,,线程B来了锁已经被删了删锁失败。
参考:redis官网lua删除锁
解决方法1(lua脚本)

def redis_lock
	lock_key = 'redis:nx:key'
	lock_val = UUID
	redis.eval("if redis.call('set', KEY[1], VAL[1], 'EX', '30', 'NX') then return 1; else return 0; end;", [lock_key], [lock_val])
	-- ##########
	-- 删除锁
	-- ##########
	del_lua = "
		if redis.call("get",KEYS[1]) == ARGV[1]
		then
    		return redis.call("del",KEYS[1])
		else
    		return 0
		end
	"
	suc_or_err = redis.eval(del_lua, [lock_key], [lock_val])
	if  suc_or_err == 1
		p '删锁成功'
	elsif suc_or_err == 0
		p '删锁失败'
	end
end

解决方法2(redis事务)
这种侵入性有点大,感觉不太好,还是推荐lua脚本,保证删锁的原子性!

def redis_lock
	lock_key = 'redis:nx:key'
	lock_val = UUID
	redis.eval("if redis.call('set', KEY[1], VAL[1], 'EX', '30', 'NX') then return 1; else return 0; end;", [lock_key], [lock_val])
	-- ##########
	-- 删除锁
	-- ##########
	if (redis.get(lock_key) == lock_val)
		redis.watch(lock_key)
		redis.multi()
		redis.del(lock_key)
		reids.exec()
	end
end

入坑4(reids集群):

如上设置,对于大部分分布式锁产生的问题都可以避免,但是还是会遇到如下的情况:

redis集群问题:

  1. 客户端A从master获取到锁
  2. 在master将锁同步到slave之前,master宕掉了。
  3. slave节点被晋级为master节点
  4. 客户端B取得了同一个资源被客户端A已经获取到的另外一个锁。安全失效!

如上的情况我们称之为脑裂

解决方案:RedLock
假设有5个redis节点,这些节点之间既没有主从,也没有集群关系。客户端用相同的key和随机值在5个节点上请求锁,请求锁的超时时间应小于锁自动释放时间。当在3个(超过半数)redis上请求到锁的时候,才算是真正获取到了锁。如果没有获取到锁,则把部分已锁的redis释放掉。

redis锁自动续期:

  • 线程A业务代码没有执行完成,后台会启动定时任务循环遍历这些锁,锁过期时间到了业务没有执行完成,自动续期。

此时有需要引入更规范,更规范的解决方式,这里官方已经给了很好的解决方式,redlock,参考官方简介:Redis分布式锁
在这里插入图片描述

Redis官方分布式锁框架:RedLock

如上针对不同语言都有对应的解决方案,这里以java的redisson为例讲解:

RedisConfig.java

@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
	/**
	* RedisConfig配置文件中添加redisson加载
	*/
    @Bean
    public Redisson redisson(){
        Config config = new Config();
        config.useSingleServer().setAddress( "redis://127.0.0.1:3306" ).setDatabase( 0 );
        return (Redisson) Redisson.create( config );
    }
}

RedisTest.java

@Autowired
    private Redisson redisson;
    private String RED_LOCK = "test:nx:lock";
    @Test
    public void testRedisson(){
        // 加锁
        RLock lock = redisson.getLock( RED_LOCK );
        lock.lock();
        try{
            // 业务代码
        } catch (Exception e){

        } finally {
            // 解锁 [lock.isLocked():锁定状态] [lock.isHeldByCurrentThread():当前线程持有锁]
            if (lock.isLocked() && lock.isHeldByCurrentThread()){
                lock.unlock();
            }
        }
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小鱼小鱼啊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值