现在很多公司都喜欢用Redis做分布式锁。
来讲一讲Redis锁的概念
1.SETNX
最早都是用SETNX 来做分布式锁
Setnx命令在指定的 key 不存在时,为 key 设置指定的值
对比SET
举2个例子:
set(“boss”,“you”);
set(“boss”,“me”);
这里执行完后boss的值是 me;
而
setNx(“boss”,“you”);
setNx(“boss”,“me”);
执行完后 boss的值是 you
因为setNx只有key 不存在的时候才会执行
所以被用来做分布式锁。只有一个线程能够执行,其余的要等这个线程删除该key 后才能继续执行。
Jedis中的写法
Long result = jedis.setnx(key, value);
返回值result :设置成功,返回 1 。设置失败,返回 0
这个就是分布式锁的简单应用
当然会出问题:
1.如果是在并发情况下线程A 已经取得锁,但是执行中挂掉了,它无法再释放锁。其他线程就永远也强不到锁。
针对这个情况可以加finally{}
2.如果在并发情况下线程A 已经取得锁,但是执行过程中整个机器挂了。finally{}都无法再执行(死锁)
这个时候就需要加超时时间了,给锁一个失效时间,到时间后立即删除
3.如果线程A执行的时间比较长,长过你设置的超时时间。那在A还是执行的时候就已经因为超时删除了锁,这个时候B线程启动抢到锁,同时A线程执行完,把B线程的锁删除。
原因就是删除其他线程的锁,这时需要设置一个ID ,删除锁前先判断是否是当前线程的ID ,如果是再删除。
如何控制锁不失效?
如果设置的锁过期时间是固定的,可能会出现执行时间过长,超过过期时间被误删。
如果不想被误删就需要设置一个动态的过期时间:
抢到锁后,启动一个其他线程来监控当前线程执行状态,每隔一定时间重置一下过期时间,只要线程还再运行就一定不会超时。
Redission 中的写法
Redisson为Java上的分布式应用程序提供了基于Redis的对象,集合,锁,同步器和服务的 分布式实现
Rlock lock = redission.getLock(“mylock”);// 获取锁
lock.lock();// 加锁
lock.unlock();// 删除锁
Redisson 简单的几句已经为你把上面提到过的问题都解决了。
但是Redission 也有自己的问题,redis主从复制时如果出现数据不一致
eg:
redis master宕机,主备切换,redis slave变为了redis master。
接着就会导致,客户端2来尝试加锁的时候,在新的redis master上完成了加锁,而客户端1也以为自己成功加了锁。
此时就会导致多个客户端对一个分布式锁完成了加锁。
这时系统在业务语义上一定会出现问题,导致各种脏数据的产生。