参考资料:https://redis.io/commands/setnx

加锁是为了解决多线程的资源共享问题。Java中,单机环境的锁可以用synchronized和Lock,其他语言也都应该有自己的加锁机制。但是到了分布式环境,单机环境中的锁就没什么作用了,因为每个节点只能获取到自己机器内存中的锁,而无法获取到其他节点的锁状态。

分布式环境中,应该用专门的分布式锁来解决需要加锁的问题。分布式锁有很多实现,Redis,zookeeper都可以。这里以Redis为例,讲述一下基于Redis的分布式锁的基本原理。

用Redis来实现分布式锁的原因

不同的节点无法获取到其他节点内存中的锁,但是大家都可以获取到Redis中的资源,所以这是实现分布式锁的基础-所有节点都可以同时获取到redis的状态。
而具体的实现,则是基于两个redis的命令-SETNX和GETSET。
SETNX:SET if Not eXists,格式为SETNX key value,仅当key不存在时才会设置成功,返回1,否则返回0。这是加锁的基础,假设key名为lock.foo,只要有一个线程设置成功,那其他线程都无法再设置。
GETSET:GETSET key value,返回旧值,并将新的值设置进去。这个的作用后面会讲到。

锁实现以及超时设计

加锁方式很简单,在线程中对redis发送一个命令:

SETNX lock.foo <current Unix time + lock timeout + 1>

 

线程A调用setnx命令,设置key为lock.foo(所有线程要用同样的key,否则就不是一个锁了),值为current Unix time + lock timeout + 1,即当前时间加上加锁时长,最终的值也就是过期时间。如果A对锁的持有结束,则可自行调用del lock.foo来释放锁。

A持有锁的过程中线程B在调用命令SETNX lock.foo,会得到返回值0,这说明这个锁已经被其他线程获取,这时B应该去获取lock.foo的值,看看是否小于当前时间,如果大于则锁未过期,B需要继续循环等待检查或者做其他操作;如果小于则锁已过期,B可以用del lock.foo方法去删除锁,然后在SETNX lock.foo 来获取锁。

这样就完成了分布式锁的最基本的模型,并且避免了因A线程挂掉无法释放锁而导致的死锁问题。

存在的问题

上一节的实现看上去大致还是那么回事,成功的加上锁了,还引入了超时机制。不过,GETSET还没用呢,这肯定还没完呢。请看以下场景:

A获取到了锁,但是挂掉了;

B和C都检测到A的锁超时;

B发出del lock.foo指令,删除A的锁,再setnx,获取到了锁;

C也发出del lock.foo指令,此时删除的是B的锁,然后再setnx,获取到了锁。

这个时候你会发现,B和C同时获取到了锁。这问题就大了去了。

为了解决这个问题,GETSET就起到他自己的作用了。下面用修正后的方法来重新描述一下上面的场景:

A获取到了锁,但是挂掉了;

B和C都检测到A的锁超时;

此时B不会执行del操作,而是执行:

GETSET lock.foo <current Unix timestamp + lock timeout + 1>

这个命令会给lock.foo设置新值,然后获取到老的value。这个时候B会对老的value进行检测,如果value大于当前时间,则说明这个锁已经被其他线程再次获取了,那B就会继续
等待,而不是获取锁。如果value小于当前时间,那B就可以获取到锁。

假设C获取到锁,然后B又再次调用GETSET方法,那也不会对C持有锁造成影响,不过确实会将超时时间延长一些。但是出现这个情况肯定是B和C都在之前检测到了锁超时,说明这两个线程对锁的访问肯定较为接近,所以这里如果要求不是太严格也可以忽略。

基本原理就这些,代码稍后奉上。

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

  1. 基于redis实现的分布式锁

    基于redis实现的分布式锁 我们知道,在多线程环境中,锁是实现共享资源互斥访问的重要机制,以保证任何时刻只有一个线程在访问共享资源.锁的基本原理是:用一个状态值表示锁,对锁的占用和释放通过状态值来标 ...

  2. 使用Redis模拟简单分布式锁,解决单点故障的问题

    需求描述: 最近做一个项目,项目中有一个功能,每天定时(凌晨1点)从数据库中获取需要爬虫的URL,并发送到对应的队列中,然后客户端监听对应的队列,然后执行任务.如果同时部署多个定时任务节点的话,每个节 ...

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

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

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

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

  5. Redlock(redis分布式锁)原理分析

    Redlock:全名叫做 Redis Distributed Lock;即使用redis实现的分布式锁: 使用场景:多个服务间保证同一时刻同一时间段内同一用户只能有一个请求(防止关键业务出现并发攻击) ...

  6. Redis分布式锁的原理和实现

    前言 我们之前聊过redis的,对基础不了解的可以移步查看一下: 几分钟搞定redis存储session共享--设计实现:https://www.cnblogs.com/xiongze520/p/10 ...

  7. Redis系列(二)--分布式锁、分布式ID简单实现及思路

    分布式锁: Redis可以实现分布式锁,只是讨论Redis的实现思路,而真的实现分布式锁,Zookeeper更加可靠 为什么使用分布式锁: 单机环境下只存在多线程,通过同步操作就可以实现对并发环境的安 ...

  8. redis客户端、分布式锁及数据一致性

    Redis Java客户端有很多的开源产品比如Redission.Jedis.lettuce等. Jedis是Redis的Java实现的客户端,其API提供了比较全面的Redis命令的支持:Redis ...

  9. Redisson 实现分布式锁的原理分析

    写在前面 在了解分布式锁具体实现方案之前,我们应该先思考一下使用分布式锁必须要考虑的一些问题.​ 互斥性:在任意时刻,只能有一个进程持有锁. 防死锁:即使有一个进程在持有锁的期间崩溃而未能主动释放锁, ...

随机推荐

  1. MySQL select语句直接导出数据

    select * into outfile '文件存放路径' from 表名; (先记下来,还未测试)

  2. Docker安装Nginx1.11.10+php7+MySQL

    Docker安装php-fpm 1.编辑Dockerfile FROM php:7.1.3-fpm ADD sources.list /etc/apt/sources.list RUN cp /usr ...

  3. JPA数据懒加载LAZY配合事务@Transactional使用(三)

    上篇博文<JPA数据懒加载LAZY和实时加载EAGER(二)>讲到,如果使用懒加载来调用关联数据,必须要保证主查询session(数据库连接会话)的生命周期没有结束,否则,你是无法抽取到数 ...

  4. SpringMvc自动装配@Controller无效

    1.问题原因:SpringMvc驱动器没有扫描该Controller层 虽然配置了 <!-- 启用spring mvc 注解 --> <context:annotation-conf ...

  5. mac idea中的Application Server was not connected before run configuration stop, reason: Unable to ping server at localhost:1099问题

    今天上午用的好好的就出现了这个问题 真是奇怪: Google了一番有以下解决办法: 1.hosts文件的问题: vi /etc/hosts文件 添加127.0.0.1  localhost里面 2.m ...

  6. 一不小心把Mysql数据库的root的账号的权限给弄没啦,该怎么办

    别急啊,现在只要你还能连接到Mysql,就问题不大! 首先,连接道Mysql,这里用Navicat进行讲解. 说明:root@localhost和root@127.0.0.1不是一个账号,也不是一回事 ...

  7. SELinux一键开启与禁用脚本

    SELinux是美国国家安全局(NSA)对于强制访问控制的实现,是 Linux历史上最杰出的新安全子系统.但是SELinux的并不能与众多服务很好的兼容,有些人会关闭SELinux一了百了.在日常的运 ...

  8. mysql字符设置

    MySQL字符集设置 mysql>CREATE DATABASE IF NOT EXISTS mydb default charset utf8 COLLATE utf8_general_ci; ...

  9. Xmind8 Pro安装教程 Windows

      xmind是一款优秀的思维导图制作软件,这一点相信不用太多解释. 尤其作为测试人员导出测试用例为excel极其方便.网上xmind的破解方法加自己琢磨,重新整理了破解步骤. 亲测可以永久激活截止2 ...

  10. linux之x86裁剪移植---grub 识别文件系统

    grub主要作用是找到内核(kernel)与部分核心模块的镜像(initrd,主要是sata硬盘驱动之类的模块),把它们导入内存中运行.kernel与initrd放在文件系统中,因此grub必须有识别 ...