内容简介:分布式锁其实可以理解为:控制分布式系统有序的去对共享资源进行操作,通过互斥来保持一致性。 举个不太恰当的例子:假设共享的资源就是一个房子,里面有各种书,分布式系统就是要进屋看书的人,分布式锁就是保证这个房子只有一个门并且一次只有一个人可以进,而且门只有一把钥匙。然后许多人要去看书,可以,排队,第一个人拿着钥匙把门打开进屋看书并且把门锁上,然后第二个人没有钥匙,那就等着,等第一个出来,然后你在拿着钥匙进去,然后就是以此类推实现分布式锁的方式有很多,只要满足上述条件的都可以实现分布式锁,比如数据库,redis,
分布式锁其实可以理解为:控制分布式系统有序的去对共享资源进行操作,通过互斥来保持一致性。 举个不太恰当的例子:假设共享的资源就是一个房子,里面有各种书,分布式系统就是要进屋看书的人,分布式锁就是保证这个房子只有一个门并且一次只有一个人可以进,而且门只有一把钥匙。然后许多人要去看书,可以,排队,第一个人拿着钥匙把门打开进屋看书并且把门锁上,然后第二个人没有钥匙,那就等着,等第一个出来,然后你在拿着钥匙进去,然后就是以此类推
实现原理
-
互斥性
保证同一时间只有一个客户端可以拿到锁,也就是可以对共享资源进行操作
-
安全性
只有加锁的服务才能有解锁权限,也就是不能让a加的锁,bcd都可以解锁,如果都能解锁那分布式锁就没啥意义了
可能出现的情况就是a去查询发现持有锁,就在准备解锁,这时候忽然a持有的锁过期了,然后b去获得锁,因为a锁过期,b拿到锁,这时候a继续执行第二步进行解锁如果不加校验,就将b持有的锁就给删除了
-
避免死锁
出现死锁就会导致后续的任何服务都拿不到锁,不能再对共享资源进行任何操作了
-
保证加锁与解锁操作是原子性操作
例:
假设a用 redis 实现分布式锁,
1,设置key
set(key,value)
2,给key设置过期时间
假设现在a刚实现set后,程序崩了就导致了没给key设置过期时间就导致key一直存在就发生了死锁
如何实现分布式锁
实现分布式锁的方式有很多,只要满足上述条件的都可以实现分布式锁,比如数据库,redis,zookeeper,在这里就先讲一下如何使用redis实现分布式锁
使用redis实现分布式锁
步骤核心:
-
使用
redis
命令set key value NX EX max-lock-time
实现加锁 -
使用
redis
命令EVAL
实现解锁
需要满足分布式锁的实现原理
加锁:
Jedis jedis = new Jedis("127.0.0.1", 6379); private static final String SUCCESS = "OK"; /** * 加锁操作 * @param key 锁标识 * @param value 客户端标识 * @param timeOut 过期时间 */ public Boolean lock(String key,String value,Long timeOut){ String var1 = jedis.set(key,value,"NX","EX",timeOut); if(LOCK_SUCCESS.equals(var1)){ return true; } return false; }
解读
-
加锁操作:
jedis.set(key,value,"NX","EX",timeOut)
,保证加锁的原子操作 -
key
就是redis
的key
值作为锁的标识,value
在这里作为客户端的标识,只有key-value
都比配才有删除锁的权利,保证安全性 -
通过
timeOut
设置过期时间保证不会出现死锁,避免死锁 -
NX
,EX
什么意思NX
:只有这个key
不存才的时候才会进行操作,if not exists
EX
:设置key
的过期时间为秒,具体时间由第5个参数决定
解锁
Jedis jedis = new Jedis("127.0.0.1", 6379); private static final Long UNLOCK_SUCCESS = 1L; /** * 解锁操作 * @param key 锁标识 * @param value 客户端标识 * @return */ public static Boolean unLock(String key,String value){ String luaScript = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then return redis.call(\"del\",KEYS[1]) else return 0 end"; Object var2 = jedis.eval(luaScript,Collections.singletonList(key), Collections.singletonList(value)); if (UNLOCK_SUCCESS == var2) { return true; } return false; }
解读
-
luaScript
这个字符串是个 lua 脚本,代表的意思是如果根据key
拿到的value
跟传入的value
相同就执行del
,否则就返回0,保证安全性 -
jedis.eval(String,list,list);
这个命令就是去执行lua
脚本,KEYS的集合就是第二个参数,ARGV
的集合就是第三参数【保证解锁的原子操作】
上述就实现了怎么使用redis去正确的实现分布式锁,但是有个小缺陷就是锁过期时间要设置为多少合适,这个其实还是需要去根据业务场景考量一下的
重试机制
上面那只是讲了加锁与解锁的操作,试想一下如果在业务中去拿锁如果没有拿到是应该阻塞着一直等待还是直接返回,这个问题其实可以写一个重试机制,根据重试次数和重试时间做一个循环去拿锁,当然这个重试的次数和时间设多少合适,是需要根据自身业务去衡量的
/** * 重试机制 * @param key 锁标识 * @param value 客户端标识 * @param timeOut 过期时间 * @param retry 重试次数 不要太大 * @param sleepTime 重试间隔时间 * @return */ public Boolean lockRetry(String key,String value,Long timeOut,Integer retry,Long sleepTime){ Boolean flag = false; try { for (int i=0;i<retry;i++){ flag = lock(key,value,timeOut); if(flag){ break; } Thread.sleep(sleepTime); } }catch (Exception e){ e.printStackTrace(); } return flag; }
redis实现分布式锁就写完了,下次用zookeeper去实现分布式锁
文中set命令详解: http://redisdoc.com/string/set.html
以上所述就是小编给大家介绍的《什么是分布式锁及正确使用redis实现分布式锁》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 【分布式锁】07-Zookeeper实现分布式锁:Semaphore、读写锁实现原理
- 原 荐 分布式锁与实现(二)基于ZooKeeper实现
- 分布式实现原理
- 实现分布式锁
- SOFAJRaft 实现原理:SOFAJRaft-RheaKV 分布式锁实现剖析
- RedLock 实现分布式锁
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Hacking Growth
Sean Ellis、Morgan Brown / Crown Business / 2017-4-25 / USD 29.00
The definitive playbook by the pioneers of Growth Hacking, one of the hottest business methodologies in Silicon Valley and beyond. It seems hard to believe today, but there was a time when Airbnb w......一起来看看 《Hacking Growth》 这本书的介绍吧!