在分布式系统中多个请求并发对少数资源进行争抢,例如10个人同时秒杀一件商品,如果不用分布式的锁进行处理(当然还有其它的处理方案),则很容易出现多个人抢到一个商品(超卖)的情况,用redis可以比较容易的实现分布式锁。(用zookeeper实现更好)
实现的大概思路是如下的:
1、读取redis的key的值,判断是否存在 -- 使用redis的exists命令
2、如果key已经存在则已经被其它用户加了锁,该线程需要等待或者直接失败(具体看业务来决定)。
3、如果key不存在则表明该线程有可能竞争到该锁。
4、使用redis的setnx命令来添加该key
setnx(key, value):如果不存在名称为key的string,则向库中添加string,名称为key,值为value
如果调用该命令返回1表示已经抢到该锁,如果返回0表示失败然后跳转到第二步处理。
此处value的值强烈建议设置为当前时间戳,一旦第5步的死锁情况发生,其它的锁竞争者可以通过判断锁的时间戳来确定是否出现了死锁,当确认是死锁(是否是死锁还得根据自己的业务的实际情况来看)的时候删除该可以,并重新开始锁的过程。
5、如果此时出现该线程所在的机器出现宕机,则处理起来比较麻烦,可以去百度去看下别人的解决方案,当然此处我们可以调用expire命令给此key设置一个超时时间,如果出现宕机的时候此命令已经执行,则造成死锁的时间也是有限的。
20170325补充:防止死锁的一种很有效的办法就是在获取锁失败后使用ttl命令查看该key是否被设置了过期时间,如果没有设置则给该keyy设置一个合理的过期时间,此处的操作可能存在并发修改的问题,但并发时的时间基本一样,所以影响基本可以忽略。
6、获得锁的线程继续执行后面的操作,执行完后的根据业务情况是否需要在finally中执行del来删除该key来及时释放对锁的占有。
用redis实现更安全的分布式锁的文章:http://ifeve.com/redis-lock/