当前位置: 首页 > news >正文

redis击穿问题使用锁实现方案

解决Redis缓存击穿问题的一种常见方法是使用互斥锁。以下是一个使用Java编写的简单示例,展示了如何利用Redis的SET命令加上NX(仅在键不存在时设置)和EX(设置键的过期时间)选项来实现分布式锁,以防止缓存击穿的情况。

步骤说明:

1. 检查缓存:首先尝试从Redis中获取数据。
2. 未命中处理:
        * 如果数据不在缓存中,尝试获取一个分布式锁。
        * 获取到锁的线程负责从数据库加载数据并写入缓存。
        * 未能获取到锁的线程可以等待一段时间后重试或者返回特定错误信息/默认值。

3. 释放锁:确保在操作完成后释放锁,避免死锁。

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;@Component
public class CacheService {private final StringRedisTemplate redisTemplate;private static final String LOCK_PREFIX = "lock:";private static final Long LOCK_EXPIRE_TIME = 5L; // 锁超时时间,单位:秒private static final String PRODUCT_KEY = "product:1";public CacheService(StringRedisTemplate redisTemplate) {this.redisTemplate = redisTemplate;}public String getProductInfo() {// 1. 尝试从Redis中获取商品信息String productInfo = redisTemplate.opsForValue().get(PRODUCT_KEY);if (productInfo != null) {return productInfo;}// 2. 获取分布式锁boolean lockAcquired = acquireLock(LOCK_PREFIX + PRODUCT_KEY);if (!lockAcquired) {// 未获取到锁,可以返回错误信息或进行重试逻辑return "Failed to acquire lock, please retry.";}try {// 重新检查缓存,防止在等待锁的过程中其他线程已经填充了缓存productInfo = redisTemplate.opsForValue().get(PRODUCT_KEY);if (productInfo != null) {return productInfo;}// 3. 从数据库加载数据productInfo = loadFromDatabase();// 4. 将数据写入RedisredisTemplate.opsForValue().set(PRODUCT_KEY, productInfo);} finally {// 5. 释放锁releaseLock(LOCK_PREFIX + PRODUCT_KEY);}return productInfo;}private boolean acquireLock(String key) {return Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(key, "lock", LOCK_EXPIRE_TIME, TimeUnit.SECONDS));}private void releaseLock(String key) {redisTemplate.delete(key);}private String loadFromDatabase() {// 这里应该是实际的数据库查询逻辑,为了示例简化,直接返回模拟数据return "Real product info from DB.";}
}

注意事项:
        1. 使用分布式锁时,务必确保锁的释放逻辑健壮,防止因异常导致锁未被正确释放。
        2. 锁的超时时间应当合理设置,过长可能导致其他线程长时间等待,过短可能在操作未完成前锁已过期。
        3. 上述示例使用了简单的字符串作为锁的标识,实际生产环境中可能需要更复杂的机制来确保锁的安全性,比如使用Redisson客户端提供的锁服务,它提供了更高级的功能,如自动续期、锁公平性等。

相关文章:

  • 零散的面试题
  • 揭示西周与汉唐时期的纺织工艺
  • 软件开发小程序正规公司流程是什么样的?
  • node通过axios调用realworld接口
  • 【UE4】角色御剑飞行的蓝图实现
  • 多模态大模型通用模式
  • Vue3、Element Plus使用v-for循环el-form表单进行校验
  • leetcode21 合并两个有序单链表
  • AI大佬都在说下一个爆点是智能体,建议开发者抢占先机!
  • java版Spring Cloud+Mybatis+Oauth2+分布式+微服务+实现工程管理系统
  • MaxKB-无需代码,30分钟创建基于大语言模型的本地知识库问答系统
  • QT QFileDialog文件选择对话框
  • 第7周作业——单片机定时器与串口通信的学习与应用
  • leetcode打卡#day45 携带研究材料(第七期模拟笔试)、518. 零钱兑换 II、377. 组合总和 Ⅳ、爬楼梯(第八期模拟笔试)
  • 概率论拾遗
  • 【css3】浏览器内核及其兼容性
  • 【跃迁之路】【477天】刻意练习系列236(2018.05.28)
  • conda常用的命令
  • gf框架之分页模块(五) - 自定义分页
  • JS笔记四:作用域、变量(函数)提升
  • Python进阶细节
  • Python中eval与exec的使用及区别
  • React的组件模式
  • Selenium实战教程系列(二)---元素定位
  • 初探 Vue 生命周期和钩子函数
  • 对象管理器(defineProperty)学习笔记
  • gunicorn工作原理
  • ​埃文科技受邀出席2024 “数据要素×”生态大会​
  • ​卜东波研究员:高观点下的少儿计算思维
  • # 深度解析 Socket 与 WebSocket:原理、区别与应用
  • (02)Hive SQL编译成MapReduce任务的过程
  • (12)Linux 常见的三种进程状态
  • (1综述)从零开始的嵌入式图像图像处理(PI+QT+OpenCV)实战演练
  • (待修改)PyG安装步骤
  • (附源码)ssm基于web技术的医务志愿者管理系统 毕业设计 100910
  • (论文阅读40-45)图像描述1
  • (三)c52学习之旅-点亮LED灯
  • (一)SvelteKit教程:hello world
  • (一)插入排序
  • * 论文笔记 【Wide Deep Learning for Recommender Systems】
  • .class文件转换.java_从一个class文件深入理解Java字节码结构
  • .NET 中 GetHashCode 的哈希值有多大概率会相同(哈希碰撞)
  • .NET的数据绑定
  • .NET牛人应该知道些什么(2):中级.NET开发人员
  • .net之微信企业号开发(一) 所使用的环境与工具以及准备工作
  • .Net转前端开发-启航篇,如何定制博客园主题
  • .stream().map与.stream().flatMap的使用
  • @RequestBody详解:用于获取请求体中的Json格式参数
  • [AutoSar]BSW_OS 01 priority ceiling protocol(PCP)
  • [BT]BUUCTF刷题第9天(3.27)
  • [C][数据结构][树]详细讲解
  • [C++从入门到精通] 14.虚函数、纯虚函数和虚析构(virtual)
  • [c++刷题]贪心算法.N01
  • [CentOs7]搭建ftp服务器(2)——添加用户
  • [CF482B]Interesting Array