Redis - Redis分布式锁

Redis分布式锁

一丶什么是分布式锁

  普通的锁,用于同一进程内不同线程在操作同一资源时,为解决冲突而加上的,使得多线程在操作统一资源时以单线程顺序执行.

  JVM的内存模型:

  主内存保存变量值, 每个线程内也有自己的内存, 一般情况下, 线程会在本内存中操作数据后,在刷入主内存, 如果多个线程都同时在各自内存操作数据后, 在刷入主内存, 可能会导致结果不正确. 如 主内存中变量a=1, 线程t1和t2, 同时读取变量a后加1, 最后刷进主内存, 则主内存a可能为2, 正确的结果为3. 如果对操作变量a的方法加上同步锁, t1先对a加1, 刷进主内存后a=2,   然后t2才对a加1, 使得主内存的结果为3.

  由于jvm的内存模型, 主内存与各线程本地内存的变量会存在差异, 各线程本地内存是相互独立的, 变量也就存在不可见性. 多线程并发运行也就容易出问题

  在分布式系统中, 在不同服务器部署不同的应用服务, 这时就存在多进程. 与多线程的问题类似, 当多进程操作同一资源时,  就需要加同步锁了. 但是普通的同步锁, 只能在同一进程中对多线程有效. 为解决这种问题, 就需要使用分布式锁了.

二丶redis分布式锁

  redis分布式锁是基于 set key value px milliseconds  nx 命令来实现的.

  其中value为随机值,一般用uuid实现. 为什么需要设置过期时间? 是为了防止拿到锁的服务, 崩溃了, 而不能正常释放锁, 导致死锁. 需要由redis服务过期删除锁.

  实现一般分为两步

  1. 使用lua脚本获取锁 (保证了原子性)  等同于set key value px milliseconds  nx (不存在key则设置)

local lockClientId = redis.call(‘GET‘, KEYS[1])
if lockClientId == ARGV[1] then
  redis.call(‘PEXPIRE‘, KEYS[1], ARGV[2])
  return true
elseif not lockClientId then
  redis.call(‘SET‘, KEYS[1], ARGV[1], ‘PX‘, ARGV[2])
  return true
end
return false

  2. 使用lua脚本释放锁

   如果value为指定随机值, 说明是该客户端获取到的锁, 则可以删除该key , 否则则直接返回 

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

三丶redis分布式锁使用场景

  在redis单机模式下,服务a获取到锁之后, redis就挂掉了,重启后,锁消失掉了,服务b就可以获取到锁,服务a和b就可以同时执行了,z这显然和设计初衷相违背.

  单机模式下, 存在单点故障, 于是使用redis集群模式, 主从同步是异步进行的, 存在延迟.  极端情况下, 服务a在主节点获取到锁后, 此时锁并未同步到从节点, 主节点就挂掉了, 在哨兵模式下, 从节点升级为主节点, 服务b可以获取到锁, 这时, 服务a和b又同时执行了.

  本质上, 分布式锁要求在同一时刻上, 只能有一个服务拿到锁, 满足CAP模型中的CP模型, 属于强一致性锁.  redis集群是为高可用出现的,属于AP模型,  所以redis分布式锁是AP模型.  但是, 为什么还会有人使用redis分布式锁呢? 脱离业务的架构都是耍流氓. 在最终一致性的业务上, 可以使用AP模型锁, 如发送消息, 重复发一条数据, 没有太大关系. 在强一致性的业务上, 需要使用CP模型锁, 如金融业务.

  贴出大佬分析的结论,此外redis还提供了另一种强一致性锁-红锁Redlock, 有兴趣可以自行研究以下

 四丶Spting提供的对redis分布式锁的支持

  1. 引入依赖包

        <dependency>
            <groupId>org.springframework.integration</groupId>
            <artifactId>spring-integration-redis</artifactId>
        </dependency>

  2. 配置RedisLockRegistry

    @Bean("defaultRedisLockRegistry")
    public RedisLockRegistry defaultRedisLockRegistry(RedisConnectionFactory connectionFactory){
        //默认1分钟超时
        RedisLockRegistry lockRegistry=new RedisLockRegistry(connectionFactory,
                RedisKeyConfig.LOCK_DEFAULT_REGISTRY_KEY);
        return lockRegistry;
    }

  3. 简单使用与测试

@SpringBootTest
public class LockTests {
    @Autowired
    RedisLockRegistry defaultRedisLockRegistry;
    private final String lockKey ="test:lock:key";

    @Autowired
    RedisManager<String,String> stringRedisManager;

    @Test
    public void shouldLock(){

        //1分钟超时
        Lock lock=defaultRedisLockRegistry.obtain(lockKey);
        boolean locked=lock.tryLock();
        if(!locked){
            return;
        }

        //获取到锁
        String lockRedisKey=RedisKeyConfig.LOCK_DEFAULT_REGISTRY_KEY+":"+lockKey;
        try {
            //一般在try{}中写业务逻辑, 报错时, finally中可以释放锁
            String clientId=stringRedisManager.get(lockRedisKey);
            Assert.state(clientId!=null && clientId.length()>0, "没有获取到redis锁");
            System.out.println("获取到redis锁(clientId:"+clientId+")");

        }catch (Exception e){
            throw new RuntimeException(e);
        }finally {
            //释放锁, 有可能之前锁已超时,被清除, 然后报错
            lock.unlock();
        }

        String clientId=stringRedisManager.get(lockRedisKey);
        Assert.state(clientId==null, "锁没有被正常释放");

    }

}

   完整源码

学习资料:

  官方文档  

   基于CAP模型设计企业级真正高可用的分布式锁

原文地址:https://www.cnblogs.com/timfruit/p/12147039.html

时间: 2024-10-03 14:50:53

Redis - Redis分布式锁的相关文章

基于Redis的分布式锁到底安全吗(上)?

网上有关Redis分布式锁的文章可谓多如牛毛了,不信的话你可以拿关键词"Redis 分布式锁"随便到哪个搜索引擎上去搜索一下就知道了.这些文章的思路大体相近,给出的实现算法也看似合乎逻辑,但当我们着手去实现它们的时候,却发现如果你越是仔细推敲,疑虑也就越来越多. 实际上,大概在一年以前,关于Redis分布式锁的安全性问题,在分布式系统专家Martin Kleppmann和Redis的作者antirez之间就发生过一场争论.由于对这个问题一直以来比较关注,所以我前些日子仔细阅读了与这场争

基于Redis实现分布式锁(转载)

原文地址:http://blog.csdn.net/ugg/article/details/41894947 Redis命令介绍使用Redis实现分布式锁,有两个重要函数需要介绍 SETNX命令(SET if Not eXists)语法:SETNX key value功能:当且仅当 key 不存在,将 key 的值设为 value ,并返回1:若给定的 key 已经存在,则 SETNX 不做任何动作,并返回0. GETSET命令语法:GETSET key value功能:将给定 key 的值设为

利用多写Redis实现分布式锁原理与实现分析

在我写这篇文章的时候,其实我还是挺纠结的,因为我这个方案本身也是雕虫小技拿出来显眼肯定会被贻笑大方,但是我最终还是拿出来与大家分享,我本着学习的态度和精神,希望大家能够给与我指导和改进方案. 一.关于分布式锁 关于分布式锁,可能绝大部分人都会或多或少涉及到. 我举二个例子: 场景一:从前端界面发起一笔支付请求,如果前端没有做防重处理,那么可能在某一个时刻会有二笔一样的单子同时到达系统后台. 场景二:在App中下订单的时候,点击确认之后,没反应,就又点击了几次.在这种情况下,如果无法保证该接口的幂

Redis实现分布式锁

http://redis.io/topics/distlock 在不同进程需要互斥地访问共享资源时,分布式锁是一种非常有用的技术手段. 有很多三方库和文章描述如何用Redis实现一个分布式锁管理器,但是这些库实现的方式差别很大,而且很多简单的实现其实只需采用稍微增加一点复杂的设计就可以获得更好的可靠性. 这篇文章的目的就是尝试提出一种官方权威的用Redis实现分布式锁管理器的算法,我们把这个算法称为RedLock,我们相信这个算法会比一般的普通方法更加安全可靠.我们也希望社区能一起分析这个算法,

基于redis的分布式锁

<?php /** * 基于redis的分布式锁 * * 参考开源代码: * http://nleach.com/post/31299575840/redis-mutex-in-php * * https://gist.github.com/nickyleach/3694555 */ pc_base::load_sys_class('cache_redis', '', 0); class dist_key_redis { //锁的超时时间 const TIMEOUT = 20; const SL

转载:基于Redis实现分布式锁

转载:基于Redis实现分布式锁  ,出处: http://blog.csdn.net/ugg/article/details/41894947 背景在很多互联网产品应用中,有些场景需要加锁处理,比如:秒杀,全局递增ID,楼层生成等等.大部分的解决方案是基于DB实现的,Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,且多客户端对Redis的连接并不存在竞争关系.其次Redis提供一些命令SETNX,GETSET,可以方便实现分布式锁机制. Redis命令介绍使用Redis实现分

基于Redis实现分布式锁

http://blog.csdn.net/ugg/article/details/41894947 背景在很多互联网产品应用中,有些场景需要加锁处理,比如:秒杀,全局递增ID,楼层生成等等.大部分的解决方案是基于DB实现的,Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,且多客户端对Redis的连接并不存在竞争关系.其次Redis提供一些命令SETNX,GETSET,可以方便实现分布式锁机制. Redis命令介绍使用Redis实现分布式锁,有两个重要函数需要介绍 SETNX命令

基于Redis实现分布式锁-Redisson使用及源码分析

在分布式场景下,有很多种情况都需要实现最终一致性.在设计远程上下文的领域事件的时候,为了保证最终一致性,在通过领域事件进行通讯的方式中,可以共享存储(领域模型和消息的持久化数据源),或者做全局XA事务(两阶段提交,数据源可分开),也可以借助消息中间件(消费者处理需要能幂等).通过Observer模式来发布领域事件可以提供很好的高并发性能,并且事件存储也能追溯更小粒度的事件数据,使各个应用系统拥有更好的自治性. 本文主要探讨另外一种实现分布式最终一致性的解决方案--采用分布式锁.基于分布式锁的解决

用redis实现分布式锁

分布式部署中不可避免用到分布式锁,目前比较常用的实现方式一般有基于数据库的乐观锁.基于redis的分布式锁和基于zookeeper的分布式锁.本文只说redis的实现方式,使用jedis作为连接器. 比较简单,直接上代码吧. public class PaasLock { private static final String KEY_NNXX = "NX"; private static final String KEY_EXPX = "PX"; private

Redis实现分布式锁原理与实现分析

一.关于分布式锁 关于分布式锁,可能绝大部分人都会或多或少涉及到. 我举二个例子: 场景一:从前端界面发起一笔支付请求,如果前端没有做防重处理,那么可能在某一个时刻会有二笔一样的单子同时到达系统后台. 场景二:在App中下订单的时候,点击确认之后,没反应,就又点击了几次.在这种情况下,如果无法保证该接口的幂等性,那么将会出现重复下单问题. 在接收消息的时候,消息推送重复.如果处理消息的接口无法保证幂等,那么重复消费消息产生的影响可能会非常大. 类似这种场景,我们有很多种方法,可以使用幂等操作,也