摘要

分布式锁在很多应用场景下是非常有效的手段,比如当运行在多个机器上的不同进程需要访问同一个竞争资源的时候,那么就会涉及到进程对资源的加锁和释放,这样才能保证数据的安全访问。分布式锁实现的方案有很多,比如基于ZooKeeper实现、或者基于Mysql实现等等,今天我们来一起看看如何基于Redis实现分布式锁服务。

分布式锁要点

对于分布式锁的目标,我们必须首先明确三点:

  1. 任何一个时间点必须只能够有一个客户端拥有锁。
  2. 不能够有死锁,也就是最终客户端都能够获得锁,尽管可能会经历失败。
  3. 错误容忍性要好,只要有大部分的Redis实例存活,客户端就应该能够获得锁。

一种简单的方法

理解了上面我们列出的三个点,我们来分析一下一般的基于Redis实现的分布式锁:

使用Redis实现锁最简单的办法是创建一个key,且这个key通常有有限的存活时间,这一点可以利用Redis的过期时间特性,所以锁最终会被释放掉,当客户端需要释放资源的时候,客户端delete这个key即可。

So far so good!但是有个单点问题,假如Redis master挂掉怎么办,因此我们需要加个slave,当master挂掉的时候可以切换到slave。这又带来了新的问题,由于Redis的复制是异步的,因此我们不能保证同时只有一个客户端获得锁。

这个模型有很显然的竞态:

  1. Client在Master上面获得了锁。
  2. master在数据同步到slave之前挂掉了。
  3. slave升级成为master。
  4. Client B申请了同样的资源的锁,成功了!

在特定条件下这种情况是会发生的,当出现多个客户端同时获得锁的时候,我们就认为可以这种锁方案是不可靠的。

基于Redis单例的实现

为了后面更好的了解分布式锁的实现,我们先来看看如何基于Redis单例实现锁服务。我们可以用下面方法获得锁:

 
 
1
SET resource_name my_random_value NX PX 30000

上面的命令在只有当key不存在的时候会执行成功(NX选项),同时会设置过期时间为30000ms(PX选项)。key的值会被设置为my_random_value。这个值在多个客户端和锁中必须是唯一的,我们使用random value是为了方便安全地释放锁,看看下面的脚本:

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

只有当key存在且值是预期的值的时候才会删除key。这种方式可以避免误删除其他客户端创建的锁。例如,当客户端获取锁之后执行一个很长时间的逻辑,一直过了锁的过期时间,这个时候锁会被自动释放掉,而另外一个客户端又获取了这个锁,前一个客户端终于执行完了逻辑执行,回头释放锁,删除key,其实这个时候释放的已经是另外一个客户端持有的锁了。使用DEL是不安全的,因为客户端有可能误删其他客户端持有的锁。上面脚本的方法的好处是每次获得锁的时候加上一个随机的签名,当释放锁的时候去看看是不是自己持有的锁,这个时候就不会误删。

现在我们学会了如何在Redis单例上获取锁和释放锁,那么接下来我们看看如何在Redis集群上获取锁和释放锁。

基于Redlock算法的实现

在分布式环境下,假设我们有N个master,这些节点都是独立的,因此我们没有配置复制策略。上面我们已经学会了如何在单机环境下获取锁和释放锁,我们假设的更具体一些,N=5,为了能获取锁,客户端的步骤为:

  1. 获取当前系统的时间,以毫秒为单位。
  2. 顺序的获取N个Redis实例上的锁,在每个实例中都用同样的key和value。在步骤2中,客户端需要一个比过期时间小很多的超时时间,例如,如果自动过期时间为10s,那么超时时间大概是5~50ms,这样可以避免客户端一直被阻塞,而不能继续请求下一个实例。
  3. 客户端每次都要计算已经过去了多长时间,使用的时间是否小于key自动过期的时间同时又获取了至少3个实例的锁。如果是,那么我们认为客户端此次获取锁成功。
  4. 如果锁被获取了,锁的过期时间必须要减去获取锁花费的时间。
  5. 如果当前客户端获取锁失败,客户端需要释放所有之前获取到的锁。

总结

这篇文章主要介绍Redis实现分布式锁的基本方法,然后分别介绍通过Redis单例和Redis集群实现分布式锁的方法。

参考文献

《Redis官方文档》

基于 Redis 实现简单的分布式锁的更多相关文章

  1. 基于Redis实现简单的分布式锁【理论】

    摘要 分布式锁在很多应用场景下是非常有效的手段,比如当运行在多个机器上的不同进程需要访问同一个竞争资源的时候,那么就会涉及到进程对资源的加锁和释放,这样才能保证数据的安全访问.分布式锁实现的方案有很多 ...

  2. 基于Redis实现简单的分布式锁

      在分布式场景下,有很多种情况都需要实现最终一致性.在设计远程上下文的领域事件的时候,为了保证最终一致性,在通过领域事件进行通讯的方式中,可以共享存储(领域模型和消息的持久化数据源),或者做全局XA ...

  3. SpringBoot基于数据库实现简单的分布式锁

    本文介绍SpringBoot基于数据库实现简单的分布式锁. 1.简介 分布式锁的方式有很多种,通常方案有: 基于mysql数据库 基于redis 基于ZooKeeper 网上的实现方式有很多,本文主要 ...

  4. 基于redis实现可靠的分布式锁

    什么是锁 今天要谈的是如何在分布式环境下实现一个全局锁,在开始之前先说说非分布式下的锁: 单机 – 单进程程序使用互斥锁mutex,解决多个线程之间的同步问题 单机 – 多进程程序使用信号量sem,解 ...

  5. 【连载】redis库存操作,分布式锁的四种实现方式[三]--基于Redis watch机制实现分布式锁

    一.redis的事务介绍 1. Redis保证一个事务中的所有命令要么都执行,要么都不执行.如果在发送EXEC命令前客户端断线了,则Redis会清空事务队列,事务中的所有命令都不会执行.而一旦客户端发 ...

  6. redis实现简单的分布式锁

    在分布式系统中多个请求并发对少数资源进行争抢,例如10个人同时秒杀一件商品,如果不用分布式的锁进行处理(当然还有其它的处理方案),则很容易出现多个人抢到一个商品(超卖)的情况,用redis可以比较容易 ...

  7. Redis整合Spring实现分布式锁

    spring把专门的数据操作独立封装在spring-data系列中,spring-data-redis是对Redis的封装 <dependencies> <!-- 添加spring- ...

  8. 【连载】redis库存操作,分布式锁的四种实现方式[一]--基于zookeeper实现分布式锁

    一.背景 在电商系统中,库存的概念一定是有的,例如配一些商品的库存,做商品秒杀活动等,而由于库存操作频繁且要求原子性操作,所以绝大多数电商系统都用Redis来实现库存的加减,最近公司项目做架构升级,以 ...

  9. 基于zookeeper简单实现分布式锁

    https://blog.csdn.net/desilting/article/details/41280869 这里利用zookeeper的EPHEMERAL_SEQUENTIAL类型节点及watc ...

随机推荐

  1. 第7/7Beta冲刺

    1.团队成员 成员姓名 成员学号 秦裕航 201731062432(组长) 刘东 201731062227 张旭 201731062129 王伟 201731062214 2.SCRU部分 2.1各成 ...

  2. 微信小程序之自定义导航栏(可实现动态添加)以及swiper(swiper-item)实现自动切换,导航标题也跟着切换

    <view class="movie-container"> <!-- 导航栏 --> <view > <scroll-view scro ...

  3. TCP协议的11种状态及其变化过程?传输的内容又是什么?

    在TCP的11种状态变迁中,我们需要用到TCP头部的三个标志位: 1.SYN,SYN=1表示这是一个连接请求报文或者连接接受报文 2.ACK,ACK=1,表示确认号生效 3.FIN,FIN=1表示发送 ...

  4. 链表习题(2)-一个集合用带头结点的单链表L表示,编写算法删除其值最大的结点。

    /*一个集合用带头结点的单链表L表示,编写算法删除其值最大的结点.*/ /* 算法思想:使用pre,p,premax,max四个指针,pre和p进行比较,premax和max进行最后的删除操作 通过遍 ...

  5. python 坑1

    目录 1.编码解码 2.基础数据类型补充: 2.1 str: 2.2list: 2.3tuple: 2.4dict: 2.5set: 3.坑 4.类型转换: 5.数据类型: 1.编码解码 编码:将文字 ...

  6. day42——外键的限制和解决方法、外键的三种约束模式、修改表(单表查询)

    day42 外键的限制和解决方法 可以添加外键关联的那个字段可以是 被唯一(unique)约束的字段 或者 主键 限制:+ 由于外键的使用,致使多个表之间产生了联系,当我们对这些表进行更新或删除操作的 ...

  7. 销售订单开票过账,admin用户和新增用户走的方法不同解决

    开发了一个功能,在销售订单开票时,生成的custInvocieJour表上的cashdisc计算时, 不加上销售订单头上的费用. 之后debug发现了在表custInvoiceJour下的initFr ...

  8. quartz2.3.0(六)job任务异常处理方式

    Job1类 package org.quartz.examples.example6; import org.quartz.DisallowConcurrentExecution; import or ...

  9. Centos7部署开源聊天软件rocket.chat

    一.部署rocket.chat 1.看官方文档部署,很简单,一步一步跟着部署即可 注意:需要部署节点需要联网主要是yum方式 https://rocket.chat/docs/installation ...

  10. Matalb中英双语手册-年少无知翻译版本

    更新: 20171207: 这是大学期间参加数模翻译的手册 正文: 愚人节快乐,突然发现自己在博客园的一篇文章.摘取如下: MATLAB 语言是一种工程语言,语法很像 VB 和 C,比 R 语言容易学 ...