整合 Redis 分布式锁:从数据结构到缓存问题解决方案
引言
在现代分布式系统中,Redis 作为高性能的键值存储系统,广泛应用于缓存、消息队列、实时计数器等多种场景。然而,在高并发和分布式环境下,如何有效地管理和控制资源访问成为一个关键问题。Redis 分布式锁正是为了解决这一问题而诞生的技术。
本文将从 Redis 的数据结构应用入手,结合 Redisson 分布式锁的实现,深入探讨如何解决常见的缓存问题(如穿透、击穿、雪崩),并提供详尽的代码示例和注释。
一、Redis 数据结构应用
Redis 提供了多种数据结构,每种数据结构都有其特定的应用场景。以下是几种常见数据结构及其典型应用场景:
1. String(字符串)
- 应用场景:适用于简单的键值存储,如用户会话、计数器等。
- 示例代码:
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service; @Service
public class CounterService {@Autowired private StringRedisTemplate stringRedisTemplate;public void incrementCounter(String key) {stringRedisTemplate.opsForValue().increment(key, 1);}public Long getCounter(String key) {return stringRedisTemplate.opsForValue().get(key); }
}
increment(key, 1):原子递增计数器。get(key):获取计数器的值。
2. List(列表)
- 应用场景:适用于队列或栈结构,如消息队列、任务队列等。
- 示例代码:
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service; @Service
public class QueueService {@Autowired private RedisTemplate<String, String> redisTemplate;public void addToQueue(String queueName, String message) {ListOperations<String, String> listOps = redisTemplate.opsForList(); listOps.rightPush(queueName, message);}public String removeFromQueue(String queueName) {ListOperations<String, String> listOps = redisTemplate.opsForList(); return listOps.leftPop(queueName); }
}
rightPush(queueName, message):将消息添加到队列尾部。leftPop(queueName):从队列头部取出消息。
3. Hash(哈希)
- 应用场景:适用于存储对象或映射表,如用户信息、商品详情等。
- 示例代码:
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service; @Service
public class UserService {@Autowired private RedisTemplate<String, Object> redisTemplate;public void saveUser(String userId, Map<String, Object> userMap) {HashOperations<String, String, Object> hashOps = redisTemplate.opsForHash(); hashOps.putAll(userId, userMap);}public Map<String, Object> getUser(String userId) {HashOperations<String, String, Object> hashOps = redisTemplate.opsForHash(); return hashOps.entries(userId); }
}
putAll(userId, userMap):将用户信息存储到哈希中。entries(userId):获取用户的完整信息。
4. Set(集合)
- 应用场景:适用于存储唯一元素的集合,如用户关注列表、标签分类等。
- 示例代码:
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service; @Service
public class TagService {@Autowired private RedisTemplate<String, String> redisTemplate;public void addTagToUser(String userId, String tag) {SetOperations<String, String> setOps = redisTemplate.opsForSet(); setOps.add(userId, tag);}public Set<String> getAllTags(String userId) {SetOperations<String, String> setOps = redisTemplate.opsForSet(); return setOps.members(userId); }
}
add(userId, tag):向用户的标签集合中添加一个标签。members(userId):获取用户的全部标签。
5. ZSet(有序集合)
- 应用场景:适用于需要排序的场景,如排行榜、优先级队列等。
- 示例代码:
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service; @Service
public class RankingService {@Autowired private RedisTemplate<String, String> redisTemplate;public void addScore(String rankingKey, String user, double score) {ZSetOperations<String, String> zSetOps = redisTemplate.opsForZSet(); zSetOps.add(rankingKey, user, score);}public Set<String> getTopUsers(String rankingKey, int limit) {ZSetOperations<String, String> zSetOps = redisTemplate.opsForZSet(); return zSetOps.reverseRange(rankingKey, 0, limit);}
}
add(rankingKey, user, score):向排行榜中添加用户及其分数。reverseRange(rankingKey, 0, limit):获取排行榜前几名的用户。
二、Redisson 分布式锁
1. 什么是 Redisson?
Redisson 是一个 Redis 的 Java 客户端,提供了许多高级功能,包括分布式锁、分布式集合、分布式消息队列等。它简化了 Redis 的使用,并提供了丰富的功能。
2. 分布式锁的应用场景
在分布式系统中,多个服务实例可能同时访问共享资源(如数据库、文件等),这可能导致数据不一致或竞争条件。分布式锁可以确保在同一时间只有一个服务实例能够访问共享资源。
3. 使用 Redisson 实现分布式锁
步骤 1:添加依赖
在 pom.xml 中添加 Redisson 依赖:
<dependencies><dependency><groupId>org.redisson</groupId> <artifactId>redisson</artifactId><version>3.17.6</version></dependency>
</dependencies>
步骤 2:配置 Redisson
在配置类中配置 Redisson 客户端:
import org.redisson.Redisson;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; @Configuration
public class RedissonConfig {@Bean public Redisson redisson() {Config config = new Config();config.useSingleServer() .setAddress("redis://localhost:6379");return Redisson.create(config); }
}
步骤 3:实现分布式锁
import org.redisson.api.RLock;
import org.redisson.api.Redisson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; @Service
public class DistributedLockService {@Autowired private Redisson redisson;public void executeWithLock(String lockName) {RLock lock = redisson.getLock(lockName); try {boolean isLocked = lock.tryLock(10, 1000, TimeUnit.MILLISECONDS);if (isLocked) {// 执行临界区代码 System.out.println("Lock acquired. Executing critical section...");Thread.sleep(2000); // 模拟耗时操作 } else {System.out.println("Failed to acquire lock.");}} catch (InterruptedException e) {Thread.currentThread().interrupt(); } finally {if (lock.isHeldByCurrentThread()) {lock.unlock(); }}}
}
tryLock(10, 1000, TimeUnit.MILLISECONDS):尝试获取锁,最长等待 10 秒,每次轮询间隔 1 秒。unlock():释放锁。
步骤 4:测试分布式锁
@RunWith(SpringRunner.class)
@SpringBootTest
public class DistributedLockServiceTest {@Autowired private DistributedLockService distributedLockService;@Test public void testDistributedLock() throws InterruptedException {// 同时启动多个线程尝试获取锁 Runnable task = () -> distributedLockService.executeWithLock("my_lock"); Thread thread1 = new Thread(task);Thread thread2 = new Thread(task);thread1.start(); thread2.start(); thread1.join(); thread2.join(); }
}
运行后,控制台将显示只有其中一个线程成功获取锁并执行临界区代码。
三、缓存问题解决方案
在实际应用中,缓存可能会遇到以下问题:
1. 缓存穿透
- 问题描述:查询一个不存在的数据,导致每次都去数据库查询。
- 解决方案:
- 缓存空值:将不存在的数据也缓存起来。
- 布隆过滤器:预先过滤不存在的数据。
示例代码(缓存空值):
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service; @Service
public class UserService {@Cacheable(value = "users", key = "#id")public User getUserById(Long id) {User user = userRepository.findById(id).orElse(null); if (user == null) {// 缓存空值 return new User();}return user;}
}
2. 缓存击穿
- 问题描述:高并发下同一个热点数据过期,导致大量请求同时访问数据库。
- 解决方案:
- 互斥锁加延迟过期:在更新缓存时加锁,避免多个请求同时更新。
- 永不过期:通过版本号或其他方式实现逻辑过期。
示例代码(互斥锁加延迟过期):
import org.redisson.api.RLock;
import org.redisson.api.Redisson;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; @Service
public class UserService {@Autowired private Redisson redisson;@Autowired private UserRepository userRepository;public User getUserById(Long id) {String key = "user:" + id;String value = redisTemplate.opsForValue().get(key); if (value != null) {return JSON.parseObject(value, User.class); }RLock lock = redisson.getLock("lock:" + id);try {boolean isLocked = lock.tryLock(10, 1000, TimeUnit.MILLISECONDS);if (isLocked) {value = redisTemplate.opsForValue().get(key); if (value != null) {return JSON.parseObject(value, User.class); }User user = userRepository.findById(id).orElse(null); if (user != null) {redisTemplate.opsForValue().set(key, JSON.toJSONString(user), 3600L, TimeUnit.SECONDS);} else {// 缓存空值 redisTemplate.opsForValue().set(key, "", 3600L, TimeUnit.SECONDS);}}} catch (InterruptedException e) {Thread.currentThread().interrupt(); } finally {if (lock.isHeldByCurrentThread()) {lock.unlock(); }}return user != null ? user : new User();}
}
3. 缓存雪崩
- 问题描述:大量缓存同时过期,导致数据库压力骤增。
- 解决方案:
- 随机过期时间:为每个缓存设置不同的过期时间。
- 永不过期:通过版本号或其他方式实现逻辑过期。
示例代码(随机过期时间):
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service; @Service
public class CacheService {@Autowired private RedisTemplate<String, Object> redisTemplate;public void setValueWithRandomExpire(String key, Object value) {long randomExpireTime = 3600L + (long) (Math.random() * 3600); // 随机过期时间(1-2小时)redisTemplate.opsForValue().set(key, value, randomExpireTime, TimeUnit.SECONDS);}
}
相关文章:
整合 Redis 分布式锁:从数据结构到缓存问题解决方案
引言 在现代分布式系统中,Redis 作为高性能的键值存储系统,广泛应用于缓存、消息队列、实时计数器等多种场景。然而,在高并发和分布式环境下,如何有效地管理和控制资源访问成为一个关键问题。Redis 分布式锁正是为了解决这一问题…...
使用AI Agents集成外部API开发智能客服解决方案(上)
生成式AI的出现已经彻底改变了传统客服,为开发者和企业提供了更快速、更准确、更个性化的响应能力。其中由大语言模型(LLM)驱动的AI代理能够分析复杂的客户咨询,访问多个数据源,并提供相关的详细答案。在本文中&#x…...
2025手机电池技术革新,
具有AlN势垒和AlGaN背势垒的硅基GaN HEMT在电池兼容电压下提供突破性的输出功率 新加坡的一个工程师团队声称,他们通过研究低压硅基GaN HEMT的双异质结构设计的潜力,开辟了新的天地。 这些研究人员认为,这类晶体管是5G频率范围2频段功率 具…...
现在中国三大运营商各自使用的哪些band频段
现在中国三大运营商4G和5G频段分配情况: 中国移动 4G频段: TD-LTE: Band 39:1880-1920MHz,实际使用1885-1915MHz。 Band 40:2300-2400MHz,实际使用2320-2370MHz。 Band 41:2515-26…...
Linux系统-centos防火墙firewalld详解
Linux系统-centos7.6 防火墙firewalld详解 1 firewalld了解 CentOS 7.6默认的防火墙管理工具是firewalld,它取代了之前的iptables防火墙。firewalld属于典型的包过滤防火墙或称之为网络层防火墙,与iptables一样,都是用来管理防火墙的工具&a…...
Java Stream API:高效数据处理的利器引言
Java Stream API:高效数据处理的利器引言 在 Java 编程中,数据处理是一项极为常见且关键的任务。传统的 for 循环在处理数据集合时,往往会导致代码变得冗长、复杂,这不仅增加了代码的编写难度,还降低了代码的可读性和…...
RK3568实战项目(六)--开发工具介绍
目录 一、引言 二、开发工具 ------>2.1、trust_merger ------>2.2、boot_merger ------>2.3、loaderimage ------>2.4、resource_tool ------>2.5、mkimage ------>2.6、mkbootimg ------>2.7、unpack_bootimg ------>2.8、repack-bootimg …...
Unity3D仿星露谷物语开发28之切换场景
1、目标 Player可以在Scene1_Farm和Scene2_Field之间自动切换。通过Trigger实现该功能。同时创建一个预设体绑定该功能,这样可以把预设体放到任何场景中,通过配置即可实现Player在Scene之间的自由切换。 2、创建场景切换的工具对象 在Hierarchy中&…...
阿里通义实验室提出AnyStory:开启个性化文本到图像生成的新篇章!
在这个数字化时代,生成式AI技术正以前所未有的速度改变着我们的创作方式。近期,阿里通义实验室发表了一篇题为《AnyStory: Towards Unified Single and Multi-Subject Personalization in Text-to-Image Generation》的论文,该论文提出了一种…...
开启蓝耘之旅:DeepSeek R1 模型在智算平台的起步教程
----------------------------------------------------------我的个人主页-------------------- 动动你的手指----------------------------------------点赞👍 收藏❤--------------------------------------------------------------- 引言 在深度学习的广袤领…...
LVS + KeepAlived 配置HA集群的步骤
LVS KeepAlived 配置HA集群的步骤 (一)集群准备 准备vmvare linux虚拟主机4台,假设对外提供的VIP是192.168.174.110 主机IP备注LVS1192.168.174.101提供4层代理-主机LVS2192.168.174.102提供4层代理-备用Apache1192.168.174.201真实服务器…...
ISAAC Sim 1.1. Isaac Sim 4.2.0 环境配置
我使用的系统是ubuntu20.04,根据推荐使用Binary installation 1.下载和安装 1.1. Isaac Sim 参照Installation using Isaac Sim Binaries — Isaac Lab Documentation完成ISAAC SIM安装。 1.2. Isaac Lab “Isaac Lab 提供您需要的工具,以便您根据项…...
活动预告 | Power Hour: Copilot 引领商业应用的未来
课程介绍 智能化时代,商业应用如何实现突破?微软全球副总裁 Charles Lamanna 将为您深度解析,剖析其中关键因素。 在本次线上研讨会中,Charles Lamanna 将分享他在增强商业运营方面的独到见解与实战策略,深度解读商业…...
Gemini 2.0模型更新:谷歌最新AI大模型全面开启智能时代
引言 2025年2月5日,谷歌人工智能实验室(Google DeepMind)发布了最新的Gemini 2.0模型系列更新,包括2.0 Flash、Flash-Lite和Pro实验版本。这些AI大模型的发布标志着人工智能技术在性能、效率和多模态能力上的进一步突破ÿ…...
前端布局与交互实现技巧
前端布局与交互实现技巧 1. 保持盒子在中间位置 在网页设计中,经常需要将某个元素居中显示。以下是一种常见的实现方式: HTML 结构 <!doctype html> <html lang"en"> <head><meta charset"UTF-8"><m…...
乘 DeepSeek 之风,破工作传统之浪,驶向智能高效新航道⭐
引言:当工作场景遭遇认知革命 在数字化转型的浪潮中,人工智能正以指数级速度重塑职场生态。DeepSeek作为国内领先的认知智能平台,其技术突破已超越简单的信息检索工具,正在演变为职场人的"第二大脑"。本文将系统解构De…...
Java | RESTful 接口规范
关注:CodingTechWork 引言 作为一名程序员,制定清晰、一致且高效的 RESTful 接口规范对于团队的开发效率和项目的长期维护至关重要。本文将详细介绍 RESTful 接口的设计理念、请求方法分类、核心规范,以及正确和错误的示例,帮助团…...
ASN.1 格式与Java类转换
ASN.1(Abstract Syntax Notation One)是一种用于描述数据结构的标准,常用于网络协议和数据编码。要将ASN.1格式的数据与Java类进行转换,通常需要使用ASN.1编译器或库来生成Java类,并使用这些类来编码和解码ASN.1数据。…...
从零开始掌握Python人工智能:实战案例、学习路径与职业建议
想必大家最近也都关注了实时,最近AI及deep seek可谓是火遍全球啊!小米总裁还说:大学生应该赶紧学会使用人工智能,你越早学会,你就比其他人更有优势。我也这样的感觉,deep seek现在可以和很多软件运用,完成绝…...
Oracle 变更redo log文件位置
更改Oracle数据库的Redo log文件位置,可以按照以下步骤操作。 1.查询当前Redo log文件信息 select * from v$log; select * from v$logfile;通过查询结果可知Redo log文件放在/oradata/redofile 目录下。 2.拷贝redo log文件到新的位置/Data/redolog $cd /orada…...
C++ 中信号转异常机制:在磁盘 I/O 内存映射场景下的应用与解析
C 中信号转异常机制:在磁盘 I/O 内存映射场景下的应用与解析 在现代 C 开发中,处理底层系统信号与高层 C 异常之间的转换是一项极具挑战性但又至关重要的任务。尤其是在涉及磁盘 I/O 操作且使用内存映射文件时,这种转换显得尤为关键。本文将围…...
Compose笔记(四)--derivedStateOf
这一节了解一下derivedStateOf,它用于创建一个派生状态。派生状态是一种特殊的状态,它的值是根据其他状态计算得出的。当依赖的状态发生变化时,派生状态会自动重新计算。使用 derivedStateOf 的主要目的是优化重组,避免不必要的重组ÿ…...
Chirpy3D:用于创意 3D 鸟类生成的连续部分潜在特征
Chirpy3D框架可以将细粒度的2D图像理解提升至3D生成的全新境界。当前的3D生成方法往往只关注于重构简单的对象,缺乏细致的特征和创造性。Chirpy3D通过结合多视角扩散模型和连续的部件潜在空间,能够生成全新且合理的3D鸟类模型。该系统不仅能够保持细致的…...
viem库
viem是一个用于和以太坊进行交互的javascript库,它提供了简单的API进行智能合约的读取和写入操作,你可以使用它来与区块链上智能合约进行交互,查询链上数据等。 基本功能 1,创建公有客户端 createPublicClient 可以创建一个链接…...
【每日一题 | 2025】2.3 ~ 2.9
个人主页:GUIQU. 归属专栏:每日一题 文章目录 1. 【2.3】P8784 [蓝桥杯 2022 省 B] 积木画2. 【2.4】P8656 [蓝桥杯 2017 国 B] 对局匹配3. 【2.5】[ABC365D] AtCoder Janken 34. 【2.6】P8703 [蓝桥杯 2019 国 B] 最优包含5. 【2.7】P8624 [蓝桥杯 2015…...
整数拆分cpp
题目如下 思路:根据数论知识,要使乘积最大化,将该数拆分成3和2即可,一直对该数进行减三操作,直到该数小于等于4即可 代码如下 谢谢观看...
DeepSeek之Api的使用(将DeepSeek的api集成到程序中)
一、DeepSeek API 的收费模式 前言:使用DeepSeek的api是收费的 免费版: 可能提供有限的免费额度(如每月一定次数的 API 调用),适合个人开发者或小规模项目。 付费版: 超出免费额度后,可能需要按…...
如何在WPS和Word/Excel中直接使用DeepSeek功能
以下是将DeepSeek功能集成到WPS中的详细步骤,无需本地部署模型,直接通过官网连接使用:1. 下载并安装OfficeAI插件 (1)访问OfficeAI插件下载地址:OfficeAI助手 - 免费办公智能AI助手, AI写作,下载…...
神经网络常见激活函数 6-RReLU函数
文章目录 RReLU函数导函数函数和导函数图像优缺点pytorch中的RReLU函数tensorflow 中的RReLU函数 RReLU 随机修正线性单元:Randomized Leaky ReLU 函数导函数 RReLU函数 R R e L U { x x ≥ 0 a x x < 0 \rm RReLU \left\{ \begin{array}{} x \quad x \ge 0…...
【分布式理论7】分布式调用之:服务间的(RPC)远程调用
文章目录 一、RPC 调用过程二、RPC 动态代理:屏蔽远程通讯细节1. 动态代理示例2. 如何将动态代理应用于 RPC 三、RPC序列化与协议编码1. RPC 序列化2. RPC 协议编码2.1. 协议编码的作用2.2. RPC 协议消息组成 四、RPC 网络传输1. 网络传输流程2. 关键优化点 一、RPC…...
