Redis读写锁和分布式锁的项目实践
笔记哥 /
05-20 /
19点赞 /
0评论 /
875阅读
## 问题1:在修改分组时,有短链接正在访问会出现什么问题?怎么解决
- 假设:现有线程A正在修改短链a的分组gid1为gid2(还未修改成功)
- 同时有一个线程B获取了短链a分组gid1,要进行统计pv,uv,uip操作时.发现gid1已经不存在了,就会发生并发问题
## 解决方案:采用读写锁
### 什么是读写锁
>
>
> Redisson读写锁是一种基于Redis实现特殊的机制,用于在分布式系统中协调对共享资源的访问,其继承了Java中的`ReentrantReadWriteLock`的思想.特别适用于读多写少的场景.**其核心是:允许多个线程同时读取共享资源,但写操作必须占用资源**.从而保证线程安全的同时提高并发性能
>
- 十分适合短链更新时:当某个线程需要更新资源时→需要获取写锁.此时所有的读操作和其他写线程会被阻塞,保证数据的一致性
# 核心原理
### 读锁(共享锁):
- 共享性:允许多个线程同时持有读锁
- 互斥性:只要存在读锁,该线程就不能获取写锁
### 写锁(排他锁):
- 独占性:同一时刻只能占有一个线程持有写锁
- 互斥性:当一个线程获取了写锁,其他线程就无法同时获取写锁和读锁.写锁占用线程修改共享资源,确保了在修改时没有其他线程访问
### 基本代码结构如下:
```java
RReadWriteLock configLock = redisson.getReadWriteLock("configLock");
// 读配置
configLock.readLock().lock();
try {
return loadConfigFromCache();
} finally {
configLock.readLock().unlock();
}
// 写配置
configLock.writeLock().lock();
try {
updateConfigInDB();
refreshCache();
} finally {
configLock.writeLock().unlock();
}
```
# 分布式锁Redisson
- 为防止缓存击穿→大量并发请求同时查询一个失效的缓存,导致数据库压力骤增
- 通过 Redisson 的 `RLock`(分布式锁)确保同一时刻只有一个线程执行数据库查询操作
## 双重检查锁→重建缓存
### **双重检查流程**
1. **第一次检查-无锁**:
- **未在代码中显式体现**:通常在实际业务中,外层会先尝试从缓存读取数据。如果缓存命中,直接返回数据,无需加锁。此处代码直接处理未命中场景,可能外层已进行第一次检查。
- **假设场景**:当缓存未命中时,请求进入加锁逻辑。
2. **加锁后第二次检查-关键**:
- 在获取分布式锁后,**再次检查缓存-**`stringRedisTemplate.opsForValue().get`。
- **目的:**确保在等待锁的过程中,其他线程可能已经更新了缓存,避免重复查询数据库。
## 缓存穿透和缓存击穿解决方法
- 流程图如下:

- 布隆过滤器+缓存空值+redisson锁
- 缓存空值的操作对布隆过滤器误判操作进行保护→防止穿透
- redisson锁操作对大量空值同时过期操作进行保护→防止击穿
```java
RLock lock = redissonClient.getLock(String.format(LOCK_SHORT_LINK_GOTO_KEY, fullShortUrl));
// 加锁
lock.lock();
try {
// 再次查询缓存
String originalUrl = stringRedisTemplate.opsForValue().get(String.format(GOTO_SHORT_LINK_KEY, fullShortUrl));
// 如果缓存中不存在,则查询数据库
if (StrUtil.isBlank(originalUrl)) {
//查询goto表
LambdaQueryWrapper linkGotoQueryWrapperqueryWrapper = Wrappers.lambdaQuery(ShortLinkGotoDO.class)
.eq(ShortLinkGotoDO::getFullShortUrl, fullShortUrl);
ShortLinkGotoDO shortLinkGotoDO = shortLinkGotoMapper.selectOne(linkGotoQueryWrapperqueryWrapper);
if (shortLinkGotoDO == null) {
//进行风控解决缓存穿透问题->设为空值
stringRedisTemplate.opsForValue().set(String.format(SHORT_LINK_IS_NULL_KEY, fullShortUrl), "-", 30, TimeUnit.MINUTES);
resp.sendRedirect("/page/notfound");
return;
}
//查询短链接表获取originUrl
LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(ShortLinkDO.class)
.eq(ShortLinkDO::getGid, shortLinkGotoDO.getGid())
.eq(ShortLinkDO::getFullShortUrl, fullShortUrl)
.eq(ShortLinkDO::getEnableStatus, 0);
ShortLinkDO shortLinkDO = baseMapper.selectOne(queryWrapper);
//数据库中不存在或者短链接已经过期则跳转404页面
if (shortLinkDO == null || (shortLinkDO.getValidDate() != null && shortLinkDO.getValidDate().before(new Date()))) {
//短链接已经过期
stringRedisTemplate.opsForValue().set(String.format(SHORT_LINK_IS_NULL_KEY, fullShortUrl), "-", 30, TimeUnit.MINUTES);
resp.sendRedirect("/page/notfound");
return;
}
// 缓存原始链接
stringRedisTemplate.opsForValue().set(String.format(GOTO_SHORT_LINK_KEY, fullShortUrl), shortLinkDO.getOriginUrl());
//预热缓存
stringRedisTemplate.opsForValue().set(
String.format(GOTO_SHORT_LINK_KEY, fullShortUrl),
shortLinkDO.getOriginUrl(),
LinkUtil.getShortLintCacheValidDate(shortLinkDO.getValidDate()),
TimeUnit.MILLISECONDS);
// 访问统计
shortLinkAccessStats(fullShortUrl, shortLinkDO.getGid(), req, resp);
// 跳转
resp.sendRedirect(shortLinkDO.getOriginUrl());
}
} finally {
lock. Unlock();
```
本文来自投稿,不代表本站立场,如若转载,请注明出处:http//www.knowhub.vip/share/2/3520
- 热门的技术博文分享
- 1 . ESP实现Web服务器
- 2 . 从零到一:打造高效的金仓社区 API 集成到 MCP 服务方案
- 3 . 使用C#构建一个同时问多个LLM并总结的小工具
- 4 . .NET 原生驾驭 AI 新基建实战系列Milvus ── 大规模 AI 应用的向量数据库首选
- 5 . 在Avalonia/C#中使用依赖注入过程记录
- 6 . [设计模式/Java] 设计模式之工厂方法模式
- 7 . 5. RabbitMQ 消息队列中 Exchanges(交换机) 的详细说明
- 8 . SQL 中的各种连接 JOIN 的区别总结!
- 9 . JavaScript 中防抖和节流的多种实现方式及应用场景
- 10 . SaltStack 远程命令执行中文乱码问题
- 11 . 推荐10个 DeepSeek 神级提示词,建议搜藏起来使用
- 12 . C#基础:枚举、数组、类型、函数等解析
- 13 . VMware平台的Ubuntu部署完全分布式Hadoop环境
- 14 . C# 多项目打包时如何将项目引用转为包依赖
- 15 . Chrome 135 版本开发者工具(DevTools)更新内容
- 16 . 从零创建npm依赖,只需执行一条命令
- 17 . 关于 Newtonsoft.Json 和 System.Text.Json 混用导致的的序列化不识别的问题
- 18 . 大模型微调实战之训练数据集准备的艺术与科学
- 19 . Windows快速安装MongoDB之Mongo实战
- 20 . 探索 C# 14 新功能:实用特性为编程带来便利