整合 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);}
}

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/70623.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

(done) openMP学习 (Day10: Tasks 原语)

url: https://dazuozcy.github.io/posts/introdution-to-openmp-intel/#19-%E6%8A%80%E8%83%BD%E8%AE%AD%E7%BB%83%E9%93%BE%E8%A1%A8%E5%92%8Copenmp 本章节内容仅提供引入&#xff0c;关于 task 更详细的细节请看 openMP 手册或者源材料 Day9 介绍了一个优化链表遍历的粗糙方…

《代码随想录第二十八天》——回溯算法理论基础、组合问题、组合总和III、电话号码的字母组合

《代码随想录第二十八天》——回溯算法理论基础、组合问题、组合总和III、电话号码的字母组合 本篇文章的所有内容仅基于C撰写。 1. 基础知识 1.1 概念 回溯是递归的副产品&#xff0c;它也是遍历树的一种方式&#xff0c;其本质是穷举。它并不高效&#xff0c;但是比暴力循…

网站快速收录策略:提升爬虫抓取效率

本文转自&#xff1a;百万收录网 原文链接&#xff1a;https://www.baiwanshoulu.com/102.html 要实现网站快速收录并提升爬虫抓取效率&#xff0c;可以从以下几个方面入手&#xff1a; 一、优化网站结构与内容 清晰的网站结构 设计简洁明了的网站导航&#xff0c;确保爬虫…

群晖安装Gitea

安装Docker Docker运行Gitea 上传gitea包&#xff0c;下载地址&#xff1a;https://download.csdn.net/download/hmxm6/90360455 打开docker 点击印象&#xff0c;点击新增&#xff0c;从文件添加 点击启动 可根据情况&#xff0c;进行高级设置&#xff0c;没有就下一步 点击应…

ES6 中函数参数的默认值

ES6 引入了函数参数的默认值&#xff08;Default Parameters&#xff09;功能&#xff0c;允许在函数定义时为某些参数提供默认值。当调用函数时&#xff0c;如果这些参数没有传递值&#xff08;或传递的值为 undefined&#xff09;&#xff0c;则会使用默认值。 1. 基本语法 …

SAP ABAP调用DeepSeek API大模型接口

搜索了一下DeepSeek&#xff0c;发现有人已经实现了SAP的对接&#xff0c; 不登录网页&#xff0c;SAP如何使用DeepSeek快速编程&#xff0c;ABAP起飞啦~ 按照对应的注册流程和方法。总算做出了第一个能够直连DeepSeek的API abap程序。 效果不错。 report ZTOOL_ABAP_CALL_D…

如何使用python制作一个天气预报系统

制作一个天气预报系统可以通过调用天气 API 来获取实时天气数据,并使用 Python 处理和展示这些数据。以下是一个完整的指南,包括代码实现和注意事项。 1. 选择天气 API 首先,需要选择一个提供天气数据的 API。常见的天气 API 有: OpenWeatherMap API:提供全球范围内的天…

verilog练习:i2c slave 模块设计

文章目录 前言1. 结构2.代码2.1 iic_slave.v2.2 sync.v2.3 wr_fsm.v2.3.1 状态机状态解释 2.4 ram.v 3. 波形展示4. 建议5. 资料总结 前言 首先就不啰嗦iic协议了&#xff0c;网上有不少资料都是叙述此协议的。 下面将是我本次设计的一些局部设计汇总&#xff0c;如果对读者有…

2025年度Python最新整理的免费股票数据API接口

在2025年这个充满变革与机遇的年份&#xff0c;随着金融市场的蓬勃发展&#xff0c;量化交易逐渐成为了投资者们追求高效、精准交易的重要手段。而在这个领域中&#xff0c;一个实时、准确、稳定的股票API无疑是每位交易者梦寐以求的工具。 现将200多个实测可用且免费的专业股票…

物品匹配问题-25寒假牛客C

登录—专业IT笔试面试备考平台_牛客网 这道题看似是在考察位运算,实则考察的是n个物品,每个物品有ai个,最多能够得到多少个物品的配对.观察题目可以得到,只有100,111,010,001(第一位是ci,第二位是ai,第三位是bi)需要进行操作,其它都是已经满足条件的对,可以假设对其中两个不同…

活动预告 |【Part 1】Microsoft 安全在线技术公开课:通过扩展检测和响应抵御威胁

课程介绍 通过 Microsoft Learn 免费参加 Microsoft 安全在线技术公开课&#xff0c;掌握创造新机遇所需的技能&#xff0c;加快对 Microsoft Cloud 技术的了解。参加我们举办的“通过扩展检测和响应抵御威胁”技术公开课活动&#xff0c;了解如何更好地在 Microsoft 365 Defen…

MySQL 库建表数量有限制吗?

问&#xff1a;MySQL 库建表数量有限制吗&#xff1f; 答&#xff1a;无限制 官方文档&#xff1a; MySQL has no limit on the number of databases. The underlying file system may have a limit on the number of directories. MySQL has no limit on the number of tabl…

【电机控制器】STC8H1K芯片——低功耗

【电机控制器】STC8H1K芯片——低功耗 文章目录 [TOC](文章目录) 前言一、芯片手册说明二、IDLE模式三、PD模式四、PD模式唤醒五、实验验证1.接线2.视频&#xff08;待填&#xff09; 六、参考资料总结 前言 使用工具&#xff1a; 1.STC仿真器烧录器 提示&#xff1a;以下是本…

校园网规划方案

个人博客站—运维鹿: http://www.kervin24.top CSDN博客—做个超努力的小奚&#xff1a; https://blog.csdn.net/qq_52914969?typeblog 本课程设计参考学习计算机网络 思科Cisco Packet Tracer仿真实验_哔哩哔哩_bilibili, 文章和pkg详见个人博客站: http://www.kervin24.to…

【redis】数据类型之list

Redis的List数据类型是一个双向链表&#xff0c;支持在链表的头部&#xff08;left&#xff09;和尾部&#xff08;right&#xff09;进行元素的插入&#xff08;push&#xff09;和弹出&#xff08;pop&#xff09;操作。这使得List既可以用作栈&#xff08;stack&#xff09;…

用 DeepSeek + Kimi 自动做 PPT,效率起飞

以下是使用 DeepSeek Kimi 自动做 PPT 的详细操作步骤&#xff1a; 利用 DeepSeek 生成 PPT 内容&#xff1a; 访问 DeepSeek 官网&#xff0c;完成注册/登录后进入对话界面。输入指令&#xff0c;例如“请用 Markdown 格式生成一份关于[具体主题]的 PPT 大纲&#xff0c;需包…

机器学习怎么学习,还有算法基本的源代码

1.scikit-learn官方文档&#xff0c;中文版/英文版 中文社区&#xff1a;https://scikit-learn.org.cn/ 中文官方文档&#xff1a;https://scikitlearn.com.cn/ 英文版&#xff1a;https://scikit-learn.org/stable/&#xff08;翻墙&#xff09; 2.菜鸟教程&#xff1a;AI&a…

记录安装pdsh中 Linux 系统根目录空间不足导致的编译安装问题

解决 Linux 系统根目录空间不足导致的编译安装问题 问题描述 在 Ubuntu 系统上编译安装 pdsh-2.29 时&#xff0c;由于根目录空间不足&#xff0c;导致 configure 过程中出现以下错误&#xff1a; ./configure: line 1197: cannot create temp file for here-document: No s…

位图的深入解析:从数据结构到图像处理与C++实现

在学习优选算法课程的时候&#xff0c;博主学习位运算了解到位运算的这个概念&#xff0c;之前没有接触过&#xff0c;就查找了相关的资料&#xff0c;丰富一下自身&#xff0c;当作课外知识来了解一下。 位图&#xff08;Bitmap&#xff09;是一种用于表示图像的数据结构&…

【Matlab优化算法-第14期】基于智能优化算法的VMD信号去噪项目实践

基于智能优化算法的VMD信号去噪项目实践 一、前言 在信号处理领域&#xff0c;噪声去除是一个关键问题&#xff0c;尤其是在处理含有高斯白噪声的复杂信号时。变分模态分解&#xff08;VMD&#xff09;作为一种新兴的信号分解方法&#xff0c;因其能够自适应地分解信号而受到…