【实战企业级Java二】渐进式理解Redis分布式锁

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 渐进式理解Redis分布式锁。分布式锁需要满足的条件互斥性、同一性、可重入性、容错性,四个条件的含义,为什么需要这个条件,如何理解分布式锁

渐进式理解Redis分布式锁

并发场景下,由于修改和保存数据的过程不是原子性的,部分操作可能会丢失,在单服务中我们常用本地锁来避免并发带来的问题。但是本地锁无法在多服务器之间生效。

1. 分布式锁需要满足的条件

  • 互斥性:任意时刻,只能有一个客户端获取锁。
  • 同一性:锁只能被持有该锁的客户端删除。
  • 可重入性:持有锁的客户端可继续对该锁加锁,实现锁的续租。
  • 容错性:持有锁的客户端下线,到期释放锁,防止死锁。

2. 如何实现Redis分布式锁?

2.1 如何使用Redis加锁❓
最直白的做法:SETNX

SETNX is short for "SET if Not eXists",即设置KEY如果不存在的话,value我们可以暂定设置1。

SETNX lockName 1

返回1说明key不存在设置成功,即获取到了锁,返回0则加锁失败。

2.2 加锁就需要解锁,使用Redis解锁❗️
删除命令:DEL
DEL lockName

删除了该key,此时其他线程就可以通过SETNX获取锁了。

2.3 为了保证容错性,需要设置锁的超时时间❗️
设置key的过期时间:EXPIRE
EXPIRE lockName 20

为key设置一个超时时间,以保证即使锁没有被显示的释放时,在到达过期时间后也能自动释放锁,防止死锁的产生。

2.4 即第一版的分布式锁伪代码为:⁉️
if(setnx(key,1) == 1){
    expire(key,30)
    try {
        work....
    } finally {
        del(key)
    }
}
2.5 问题1:加锁和设置过期时间是非原子操作❗️

在极端情况下,当线程执行完SETNX还未执行EXPIRE时服务挂掉。

此时该锁既不会被显示的解锁,也不会自动过期,其他线程再也无法获取到该锁了,game over。

2.6 如何解决死锁的问题呢❓
SET命令加锁
SET lockName 1 EX 30

SETNX命令是不支持传入超时时间的,不过幸好Redis2.6.12以后为SET指令增加了可选参数EX、PX属性,这样加锁和设置超时时间就是原子操作了。

2.7 问题2:锁到期,任务未完成❗️

回忆一下我们实现的锁机制,如果锁到期了任务未完成将产生两个严重问题。

image-20220802183924210.png

  1. 将其他线程的锁释放(不满足同一性)。
  2. 其他线程提前获取到了锁,即本不应该同时执行的任务同事执行(不满足互斥性)。
2.8 如何解决释放其他线程锁的问题❓

解决这个问题,我们只需要在删除之前验证key对应的value是不是自己的线程。

我们可以把线程ID作为key对应的value,在删除之前验证一下锁是不是自己的锁。

伪代码:

加锁:
String threadId = Thread.currentThread().getId()
set(key,threadId ,30,EX)
解锁:
if(threadId .equals(redisClient.get(key))){
    del(key)
}

这里,判断锁和删除锁是两个独立操作,不是原子操作。

我们可以使用lua脚本来实现:

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

这样,判断和删除过程就是原子操作了。

2.9 如果解决两个线程同时获取到锁的问题❓

上面我们解决了释放非自己锁的问题,但是AB两个线程同时执行任务也是不完美的。

我们可以让获得锁的线程开启一个守护线程,用来给快到期的锁续期。

image-20220803010521389.png

3. 下一篇Redisson分布式锁

Redis分布式锁在生产中使用自然不需要我们自己去实现每一个细节,Redis分布式锁在java中的解决方案官方推荐就是Redisson

【Distributed Locks with Redis】

🏄🏻作者简介:CSDN博客专家,华为云云享专家,阿里云专家博主,疯狂coding的普通码农一枚

🚴🏻‍♂️个人主页:莫逸风

🇨🇳喜欢文章欢迎大家👍🏻点赞🙏🏻关注⭐️收藏📄评论↗️转发

🏋️‍♂️公众号:莫逸风

📱微信:moyifengxue

目录
相关文章
|
3月前
|
存储 负载均衡 NoSQL
【赵渝强老师】Redis Cluster分布式集群
Redis Cluster是Redis的分布式存储解决方案,通过哈希槽(slot)实现数据分片,支持水平扩展,具备高可用性和负载均衡能力,适用于大规模数据场景。
279 2
|
3月前
|
存储 缓存 NoSQL
【📕分布式锁通关指南 12】源码剖析redisson如何利用Redis数据结构实现Semaphore和CountDownLatch
本文解析 Redisson 如何通过 Redis 实现分布式信号量(RSemaphore)与倒数闩(RCountDownLatch),利用 Lua 脚本与原子操作保障分布式环境下的同步控制,帮助开发者更好地理解其原理与应用。
204 6
|
3月前
|
Java 数据库连接 Android开发
Java:历久弥新的企业级编程语言
Java:历久弥新的企业级编程语言
|
4月前
|
存储 缓存 NoSQL
Redis核心数据结构与分布式锁实现详解
Redis 是高性能键值数据库,支持多种数据结构,如字符串、列表、集合、哈希、有序集合等,广泛用于缓存、消息队列和实时数据处理。本文详解其核心数据结构及分布式锁实现,帮助开发者提升系统性能与并发控制能力。
|
2月前
|
存储 SQL NoSQL
Redis-常用语法以及java互联实践案例
本文详细介绍了Redis的数据结构、常用命令及其Java客户端的使用,涵盖String、Hash、List、Set、SortedSet等数据类型及操作,同时提供了Jedis和Spring Boot Data Redis的实战示例,帮助开发者快速掌握Redis在实际项目中的应用。
234 1
Redis-常用语法以及java互联实践案例
|
2月前
|
IDE 安全 Java
Lombok 在企业级 Java 项目中的隐性成本:便利背后的取舍之道
Lombok虽能简化Java代码,但其“魔法”特性易破坏封装、影响可维护性,隐藏调试难题,且与JPA等框架存在兼容风险。企业级项目应优先考虑IDE生成、Java Records或MapStruct等更透明、稳健的替代方案,平衡开发效率与系统长期稳定性。
157 1
|
1月前
|
机器学习/深度学习 人工智能 监控
Java与AI模型部署:构建企业级模型服务与生命周期管理平台
随着企业AI模型数量的快速增长,模型部署与生命周期管理成为确保AI应用稳定运行的关键。本文深入探讨如何使用Java生态构建一个企业级的模型服务平台,实现模型的版本控制、A/B测试、灰度发布、监控与回滚。通过集成Spring Boot、Kubernetes、MLflow和监控工具,我们将展示如何构建一个高可用、可扩展的模型服务架构,为大规模AI应用提供坚实的运维基础。
171 0
|
1月前
|
机器学习/深度学习 分布式计算 Java
Java与图神经网络:构建企业级知识图谱与智能推理系统
图神经网络(GNN)作为处理非欧几里得数据的前沿技术,正成为企业知识管理和智能推理的核心引擎。本文深入探讨如何在Java生态中构建基于GNN的知识图谱系统,涵盖从图数据建模、GNN模型集成、分布式图计算到实时推理的全流程。通过具体的代码实现和架构设计,展示如何将先进的图神经网络技术融入传统Java企业应用,为构建下一代智能决策系统提供完整解决方案。
236 0
|
2月前
|
NoSQL Java 调度
分布式锁与分布式锁使用 Redis 和 Spring Boot 进行调度锁(不带 ShedLock)
分布式锁是分布式系统中用于同步多节点访问共享资源的机制,防止并发操作带来的冲突。本文介绍了基于Spring Boot和Redis实现分布式锁的技术方案,涵盖锁的获取与释放、Redis配置、服务调度及多实例运行等内容,通过Docker Compose搭建环境,验证了锁的有效性与互斥特性。
166 0
分布式锁与分布式锁使用 Redis 和 Spring Boot 进行调度锁(不带 ShedLock)
|
2月前
|
缓存 NoSQL 关系型数据库
Redis缓存和分布式锁
Redis 是一种高性能的键值存储系统,广泛用于缓存、消息队列和内存数据库。其典型应用包括缓解关系型数据库压力,通过缓存热点数据提高查询效率,支持高并发访问。此外,Redis 还可用于实现分布式锁,解决分布式系统中的资源竞争问题。文章还探讨了缓存的更新策略、缓存穿透与雪崩的解决方案,以及 Redlock 算法等关键技术。

热门文章

最新文章