Redis分布式锁

原文:https://redis.io/topics/distlock
翻译:http://udn.yyuap.com/article-6007.html

缓存主要目的是加快查询的速度,最好不要频繁的改动。

但现在有个需求,网站每个店铺的访问量,这个数值,是频繁变动的,也放到redis中了。而且用分布式部署。

对这个数值的操作是读出来,加一,再写回去。这三步应该是原子操作,不应该多个线程交叉进行,否则会出现错误。

如果没有分布式,代码只在一个机器上运行,那么可以在这段代码上加上synchronized关键字保证线程同步。

其实synchronized也就是将一个对象作为锁,线程要想执行代码,必须先获得锁,同一时间只有一个线程有锁,就可以保证线程同步。
参考:https://www.cnblogs.com/QQParadise/articles/5059824.html(synchronized)

现在是分布式系统,一份代码部署到了几台机器上,要想实现线程同步,同样需要一把锁,这个锁可以放到redis中。
参考:https://redis.io/commands/set(set命令)
https://redis.io/commands/setnx(setnx命令)

如果只有一条redis服务器,可以这样做:
对每个店铺设一个锁

SET store_id current_time_mills NX PX 30000

如果store_id不存在,就将它的值设为一个唯一值,设置过期时间为30s,返回值为“OK”,获得锁。如果store_id已经存在,就什么都不做,返回值为“NULL”,没有获得锁,等待,再试。
这里写图片描述

如上图所示,一个线程获得锁后,分几种情况,一个是线程出错了,他没有正常结束,这样锁到期后,会自动打开;还有一种情况是线程正常执行,执行完后要释放锁,释放锁,就是将store_id关键词删除。这又分两种情况,一种是执行时间比锁过期时间短,执行完,删除锁,没有问题;还有一种情况是执行时间比较长,超过了锁过期时间,这是要是删除的话,可能就是删除别的线程的锁了,这样就会出现多个线程同时执行。所以在删除时简单判断下,就可以避免这种情况:

if redis.call("get",KEYS[1]) == ARGV[1]
then
    return redis.call("del",KEYS[1])
else
    return 0
end

判断值是不是当初设的值(所有这个值要唯一),如果是删除,如果不是什么都不做。

如果只有一个redis,这个redis崩溃后,同步机制就瘫痪了。多个redis保证安全。
多个redis是这样做的:
Redlock算法:

  1. 记录当前时间,以毫秒为单位;
  2. 以串行的方式尝试从所有的N个实例中获取锁,使用的是相同的key值和相同的随机的value值。在从每个redis实例获取锁时,客户端会设置一个连接超时,其时长相比锁的自动释放时间要短的多。例如,如果锁的自动释放时间是10s,那么连接超时大概设置在5~50ms之间。这可以避免当Redis节点关掉时,会长时间堵住客户端,如果这个节点没及时响应,就应该尽快转到下个节点。
  3. 客户端计算获取所有锁耗费的时长,方法是当前时间减去步骤1中的时间戳。当且仅当客户端能从多数节点(n/2+1)中获得锁,并且耗费时长小于锁的有效期时,可以认为锁已经获得了。
  4. 如果锁获得了,它的最终有效时长将重新计算为远时长将去步骤3中获取锁耗费的时长;
  5. 如果锁获取失败了(要么是没有锁住N/2+1个节点,要么是锁的最终有效时长为负数),客户端会对所有实例进行解锁操作(即时对没有加锁成功的实例也一样)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值