返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >带你轻松掌握Redis分布式锁
  • 403
分享到

带你轻松掌握Redis分布式锁

2024-04-02 19:04:59 403人浏览 八月长安
摘要

目录1. 什么是分布式锁2. 分布式锁该具备的特性3. 基于数据库做分布式锁4. 基于Redis做分布式锁4.1 超时问题4.2 可重入锁4.3 集群环境的缺陷4.4 Redlock

目前很多大型网站及应用都是分布式部署的,分布式场景中的数据一致性问题一直是一个比较重要的话题。

基于 CAP理论,任何一个分布式系统都无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance),最多只能同时满足两项。

我们为了保证数据的最终一致性,需要很多的技术方案来支持,比如分布式事务、分布式锁等。通常大家都会采redis做分布式锁,但这样就可以高枕无忧了吗?

1. 什么是分布式锁

分布式与单机情况下最大的不同在于其不是多线程而是多进程,而数据只有一份(或有限制),也就是说单机的共享内存已解决不了一致性写问题,此时需要利用锁的技术控制某一时刻修改数据的进程数。

当在分布式模型下,分布式锁还是可以将标记存在内存,只是该内存不是某个进程分配的内存而是公共内存(Redis、Memcache)。至于利用数据库、文件等做锁与单机的实现是一样的,只要保证标记能互斥就行。

2. 分布式锁该具备的特性

  • 最好是可重入锁(避免死锁)
  • 最好是一把阻塞锁(根据业务需求决定)
  • 最好是一把公平锁(根据业务需求决定)
  • 高可用、高性能的获取锁和释放锁功能

3. 基于数据库做分布式锁

  • 基于乐观锁,CAS,但如果是insert的情况采用主键冲突防重,在大并发情况下有可能会造成锁表现象
  • 基于悲观锁,也就是排他锁,会有各种各样的问题(操作数据库需要一定的开销,使用数据库的行级锁并不一定靠谱,性能不靠谱)

如果按分布式该具备的特性来逐条匹配,特别是高可用(存在单点)、高性能是硬伤

4. 基于Redis做分布式锁

一般都使用 setnx(set if not exists) 指令,只允许被一个客户端占有,先来先得, 用完后再通过 del 指令释放。

如果中间逻辑执行时发生异常,可能会导致 del 指令没有被执行,这样就会陷入死锁,怎么破?

对,给锁加个过期时间(即使出现异常也可以保证几秒之后锁会自动释放)!

但setnx 和 expire 之间redis服务器突然挂掉,怎么破?

其实该问题的根源就在于 setnx 和 expire 是两条指令而不是原子指令。为了解决这个疑难,Redis 开源社区涌现了一堆分布式锁的 解决方案。为了治理这个乱象,Redis 2.8 版本中加入了 set 指令的扩展参数,使得 setnx 和 expire 指令可以一起执行,彻底解决了分布式锁的乱象。

总之,setnx 和 expire 组合就是分布式锁的奥义所在。

4.1 超时问题

如果在加锁和释放锁之间的逻辑执行的太长,超出了超时限制,怎么破?

也就是说第一个线程持有的锁过期了但临界区的逻辑还没有执行完,这个时候第二个线程就提前重新持有了这把锁,导致每个请求执行临界区代码时不能严格的串行执行。

Redis 的分布式锁不能解决超时问题,建议分布式锁不要用于较长时间的任务。

稍微安全一点的方案是为 set 指令的 value 参数设置为一个随机数,释放锁时先匹配随机数是否一致,一致的话再删除 key,这是可以确保当前线程占有的锁不会被其它线程释放,但是并不能解决锁被redis服务器自动释放的。


int tag = random.nextint()//随机数
boolean nx=true;
int ex=5;
if(redis.set(key, tag, nx, ex)){
    do_something()
    redis.delifequals(key, tag)//不存在这样的命令
}

但是匹配 value 和删除 key 不是一个原子操作,怎么破?

需要使用 lua 脚本来处理了,因为 Lua 脚本可以保证连续多个指令的原子性执行。


#delifequals.lua文件,下面的是社区热门代码
if redis.call('get', KEYS[1]) == ARGV[1] then
    return redis.call('del', KEYS[1])
else
    return 0
end
//java调用
public void delifequals(){
    String script = readScript("delifequals.lua");
    int tag = 5;
    String key = "key";
    Object eval = jedis.eval(script, Lists.newArrayList(key), Lists.newArrayList(tag));
    System.out.println(eval);
}

4.2 可重入锁

redis有类似Java 语言里有个 ReentrantLock 就是可重入锁吗?

要支持可重入,需要对jedis 的 set 方法进行包装,思路是:使用 Threadlocal 存储当前持有锁的计数。可重入锁加重了客户端的复杂性,精确一点还需要考虑内存锁计数的过期时间,代码复杂度将会继续升高。


public class JedisWithReentrantLock {
    private Jedis jedis;
    
    private ThreadLocal<Map<String, Integer>> lockers = new ThreadLocal<>();
    public JedisWithReentrantLock(Jedis jedis) {
        this.jedis = jedis;
    }
    private boolean set(String key) {
        return jedis.set(key, "", "nx", "ex", 5L) != null;
    }
    private void del(String key) {
        jedis.del(key);
    }
    private Map<String, Integer> getLockers() {
        Map<String, Integer> refs = lockers.get();
        if (refs != null) {
            return refs;
        }
        lockers.set(Maps.newHashMap());
        return lockers.get();
    }
 
    public boolean lock(String key) {
        Map<String, Integer> refs = getLockers();
        Integer refCount = refs.get(key);
        if (refCount != null) {
            refs.put(key, refCount + 1);
            return true;
        }
        if (!this.set(key)) {
            return false;
        }
        refs.put(key, 1);
        return true;
    }
 
    public boolean unlock(String key) {
        Map<String, Integer> refs = getLockers();
        Integer refCount = refs.get(key);
        if (refCount == null) {
            return false;
        }
        refCount -= 1;
        if (refCount > 0) {
            refs.put(key, refCount);
        } else {
            refs.remove(key);
            this.del(key);
        }
        return true;
    }
}
    @Test
    public void runJedisWithReentrantLock() {
        JedisWithReentrantLock redis = new JedisWithReentrantLock(jedis);
        System.out.println(redis.lock("alex"));
        System.out.println(redis.lock("alex"));
        System.out.println(redis.unlock("alex"));
        System.out.println(redis.unlock("alex"));
    }

4.3 集群环境的缺陷

在集群环境下,这种方式是有缺陷的(数据不一致的情况)。比如在 Sentinel 集群中,主节点挂掉时(原先第一个客户端在主节点中申请成功了一把锁),从节点A 会取而代之并晋升为主(但是这把锁还没有来得及同步),虽然客户端上却并没有明显感知,但是这时另一个客户端过来请求 从节点A 可以成功加锁,这样就会导致系统中同样一把锁被两个客户端同时持有。

主从发生故障转移,一般持续时间极短,数据不一致的情况基本上都是小概率事件。

4.4 Redlock

上面的集群同步问题导致的缺陷,难道就没有解决方案吗?

为此Antirez 发明了 Redlock 算法,它的流程比较复杂,不过已经有了很多开源的实现。

原理

使用 Redlock,需要提供多个 Redis 实例,这些实例之前相互独立没有主从关系。同很多分布式算法一样,redlock 也使用少数服从多数。

加锁时,它会向过半节点发送 set(key, value, nx, ex) 指令,只要过半节点 set 成功,那就认为加锁成功。释放锁时,需要向所有节点发送 del 指令。缺陷:因为 Redlock 需要向多个节点进行读写,意味着相比单实例 Redis 性能会下降一些。

注:Redlock算法还需要考虑出错重试、时钟漂移等很多细节问题

使用场景

如果你很在乎高可用性,希望挂了一台 redis 完全不受影响,那就应该考虑 redlock。

引用资料

How to do distributed locking

Redlock的实现

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

--结束END--

本文标题: 带你轻松掌握Redis分布式锁

本文链接: https://lsjlt.com/news/156815.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 带你轻松掌握Redis分布式锁
    目录1. 什么是分布式锁2. 分布式锁该具备的特性3. 基于数据库做分布式锁4. 基于Redis做分布式锁4.1 超时问题4.2 可重入锁4.3 集群环境的缺陷4.4 Redlock...
    99+
    2024-04-02
  • 一文带你轻松掌握Promise
    回调函数回调函数的定义非常简单:一个函数被当做一个实参传入到另一个函数(外部函数),并且这个函数在外部函数内被调用,用来完成某些任务的函数。就称为回调函数回调函数的两种写法(实现效果相同):const text = () => { ...
    99+
    2023-05-14
    javascript promise
  • 一文带你搞懂Redis分布式锁
    目录1、分布式锁简介2、setnx3、Redis-分布式锁-阶段14、Redis-分布式锁-阶段25、Redis-分布式锁-阶段36、Redis-分布式锁-阶段47、Redis-分布...
    99+
    2024-04-02
  • 《GO分布式教程:索引技巧大揭秘,轻松掌握分布式搜索!》
    随着互联网的不断发展和云计算技术的普及,分布式系统已经成为了现代软件系统中的重要组成部分。在这些分布式系统中,可以使用分布式搜索技术来实现快速的数据检索和查询。而GO语言作为一种高效、可靠、并发性强的编程语言,已经成为了分布式搜索领域中的...
    99+
    2023-06-08
    分布式 教程 索引
  • JavaScript Webpack:入门指南,带你轻松掌握Webpack的奥秘
    Webpack 是一个现代 JavaScript 模块化构建工具,它可以帮助您将多个 JavaScript 模块打包成一个或多个较小的 bundle,从而提高应用程序的性能和加载速度。Webpack 还提供了许多其他有用的特性,如代码分...
    99+
    2024-02-08
    Webpack JavaScript 模块化 构建工具
  • 玩转Redis-老板带你深入理解分布式锁
    老板:我们每天不都在经历分布式锁吗,我来给你回忆回忆。 小猿:好勒,瓜子板凳已备好。 本文结构 为什么要使用分布式锁 分布式锁有哪些特点 分布式锁流行算法及其优缺点 基本算法 relock算法 token算法 数据库排它锁、ZooKee...
    99+
    2020-02-11
    玩转Redis-老板带你深入理解分布式锁
  • 架构师带你玩转分布式锁
    大多数互联网系统都是分布式部署的,分布式部署确实能带来性能和效率上的提升,但为此,我们就需要多解决一个分布式环境下,数据一致性的问题。当某个资源在多系统之间,具有共享性的时候,为了保证大家访问这个资源数据是一致的,那么就必须要求在同一时刻只...
    99+
    2023-06-05
  • 轻松掌握PHP处理大数据的方法!教程带你飞!
    PHP是一种流行的开发语言,不仅用于Web开发,还用于处理大数据。在本篇文章中,我们将分享一些处理大数据的方法,让你轻松掌握PHP的大数据处理技巧。 一、使用内存缓存 处理大数据时,内存缓存是一种非常有效的方法,它可以大大提高处理速度。PH...
    99+
    2023-10-04
    大数据 教程 自然语言处理
  • GO语言分布式教程,你掌握了吗?
    随着互联网的发展,分布式系统已经成为了现代软件开发不可或缺的一部分。而GO语言作为一门优秀的编程语言,其天生支持并发和分布式,成为了分布式系统开发的首选语言。本文将为大家介绍GO语言分布式的基本概念和实践技巧,帮助大家更好地掌握GO语言在...
    99+
    2023-06-29
    分布式 面试 教程
  • 如何掌握分布式Paxos
    这篇文章主要讲解了“如何掌握分布式Paxos”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何掌握分布式Paxos”吧!Paxos 算法Paxos 是分布式...
    99+
    2024-04-02
  • redis分布式锁与zk分布式锁的对比分析
    目录Redis实现分布式锁原理能实现的锁类型注意事项 zk实现分布式锁原理能实现的锁类型两种锁的对比在分布式环境下,传统的jvm级别的锁会失效,那么分布式锁就是非常有必要的一个技术,一般我们可以通过redis,...
    99+
    2022-11-18
    redis分布式锁 分布式锁 zk分布式锁
  • 十分钟轻松掌握dataframe数据选择
    数据初始化 import pandas as pd import numpy as np a=np.array([['北京','北方','一线','非沿海'],['杭州','南...
    99+
    2024-04-02
  • Redis——》实现分布式锁
    推荐链接:     总结——》【Java】     总结——》【Mysql】     总结——》【Redis】     总结——》【Kafka】     总结——》【Spring】     总结—...
    99+
    2023-09-03
    redis 分布式 过期 lua
  • Redis实现分布式锁
    单体锁存在的问题 在单体应用中,如果我们对共享数据不进行加锁操作,多线程操作共享数据时会出现数据一致性问题。 (下述实例是一个简单的下单问题:从redis中获取库存,检查库存是否够,>0才允许下单) 我们的解决办法通常是加锁。如下加单体锁...
    99+
    2023-08-16
    分布式 java jvm
  • 轻松掌握python设计模式之策略模式
    本文实例为大家分享了python策略模式代码,供大家参考,具体内容如下 """ 策略模式 """ import types class StrategyExample: def __init__(s...
    99+
    2022-06-04
    模式 策略 轻松
  • 详解PHP分页原理及实现方法,让你轻松掌握
    PHP分页是网页开发中常见的功能,特别是在展示大量数据时,为了避免界面混乱和提升用户体验,分页功能显得尤为重要。本文将详细解释PHP分页的原理及实现方法,并提供具体的代码示例,帮助读者...
    99+
    2024-02-29
    分页 php 实现
  • 掌握Python运算符的详细解析:使你轻松掌握Python运算符
    Python运算符详解:让你轻松掌握Python运算符,需要具体代码示例 引言:Python是一种简单而强大的编程语言,它提供了许多运算符,使我们能够轻松进行各种计算。本文将详细讲解Python中常用的运算符,并通过具体的代码示...
    99+
    2024-01-20
    轻松掌握
  • 轻松掌握python设计模式之访问者模式
    本文实例为大家分享了python访问者模式代码,供大家参考,具体内容如下 """访问者模式""" class Node(object): pass class A(Node): pass c...
    99+
    2022-06-04
    模式 访问者 轻松
  • ASP 分布式编程算法,你是否能够掌握?
    ASP (Answer Set Programming) 是一种基于逻辑的知识表示和推理技术,它可以用来解决很多复杂的问题。随着分布式计算的流行,ASP 分布式编程算法也越来越受到关注。本文将介绍 ASP 分布式编程算法的原理和应用,并提...
    99+
    2023-08-07
    分布式 教程 编程算法
  • Node.js Promises 入门:让你轻松掌握非阻塞编程
    Node.js Promises 入门 Node.js 中的 Promise 是一种用于处理异步操作的强大工具,它可以帮助您轻松实现非阻塞编程,提高代码的可读性和可维护性。本指南将带您了解 Promise 的基本概念、用法和一些常见示例...
    99+
    2024-02-12
    Node.js Promises 异步编程 非阻塞编程 代码可读性 可维护性
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作