问题1:在修改分组时,有短链接正在访问会出现什么问题?怎么解决

  • 假设:现有线程A正在修改短链a的分组gid1为gid2(还未修改成功)
  • 同时有一个线程B获取了短链a分组gid1,要进行统计pv,uv,uip操作时.发现gid1已经不存在了,就会发生并发问题

解决方案:采用读写锁

什么是读写锁

Redisson读写锁是一种基于Redis实现特殊的机制,用于在分布式系统中协调对共享资源的访问,其继承了Java中的ReentrantReadWriteLock的思想.特别适用于读多写少的场景.其核心是:允许多个线程同时读取共享资源,但写操作必须占用资源.从而保证线程安全的同时提高并发性能

  • 十分适合短链更新时:当某个线程需要更新资源时→需要获取写锁.此时所有的读操作和其他写线程会被阻塞,保证数据的一致性

核心原理

读锁(共享锁):

  • 共享性:允许多个线程同时持有读锁
  • 互斥性:只要存在读锁,该线程就不能获取写锁

写锁(排他锁):

  • 独占性:同一时刻只能占有一个线程持有写锁
  • 互斥性:当一个线程获取了写锁,其他线程就无法同时获取写锁和读锁.写锁占用线程修改共享资源,确保了在修改时没有其他线程访问

基本代码结构如下:

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锁操作对大量空值同时过期操作进行保护→防止击穿
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<ShortLinkGotoDO> 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<ShortLinkDO> 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();

Redisson读写锁和分布式锁的项目实践的更多相关文章

  1. Redisson实现分布式锁(3)—项目落地实现

    Redisson实现分布式锁(3)-项目落地实现 有关Redisson实现分布式锁前面写了两篇博客作为该项目落地的铺垫. 1.Redisson实现分布式锁(1)---原理 2.Redisson实现分布 ...

  2. 二、多线程基础-乐观锁_悲观锁_重入锁_读写锁_CAS无锁机制_自旋锁

    1.10乐观锁_悲观锁_重入锁_读写锁_CAS无锁机制_自旋锁1)乐观锁:就像它的名字一样,对于并发间操作产生的线程安全问题持乐观状态,乐观锁认为竞争不总是会发生,因此它不需要持有锁,将 比较-设置 ...

  3. 通俗易懂 悲观锁、乐观锁、可重入锁、自旋锁、偏向锁、轻量/重量级锁、读写锁、各种锁及其Java实现!

    网上关于Java中锁的话题可以说资料相当丰富,但相关内容总感觉是一大串术语的罗列,让人云里雾里,读完就忘.本文希望能为Java新人做一篇通俗易懂的整合,旨在消除对各种各样锁的术语的恐惧感,对每种锁的底 ...

  4. AQS系列(四)- ReentrantReadWriteLock读写锁的释放锁

    前言 继续JUC包中ReentrantReadWriteLock的学习,今天学习释放锁. 一.写锁释放锁 入口方法 public void unlock() { sync.release(1); } ...

  5. 写文章 通俗易懂 悲观锁、乐观锁、可重入锁、自旋锁、偏向锁、轻量/重量级锁、读写锁、各种锁及其Java实现!

    网上关于Java中锁的话题可以说资料相当丰富,但相关内容总感觉是一大串术语的罗列,让人云里雾里,读完就忘.本文希望能为Java新人做一篇通俗易懂的整合,旨在消除对各种各样锁的术语的恐惧感,对每种锁的底 ...

  6. Redisson源码解读-分布式锁

    前言 Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid).Redisson有一样功能是可重入的分布式锁.本文来讨论一下这个功能的特点以及源 ...

  7. spring boot 利用redisson实现redis的分布式锁

    原文:http://liaoke0123.iteye.com/blog/2375469 利用redis实现分布式锁,网上搜索的大部分是使用java jedis实现的. redis官方推荐的分布式锁实现 ...

  8. JAVA锁机制-可重入锁,可中断锁,公平锁,读写锁,自旋锁,

    如果需要查看具体的synchronized和lock的实现原理,请参考:解决多线程安全问题-无非两个方法synchronized和lock 具体原理(百度) 在并发编程中,经常遇到多个线程访问同一个 ...

  9. 多线程编程之Apue3rd_Chapter11之互斥锁_读写锁_自旋锁

    学习了apue3rd的第11章,主要讲的是多线程编程.因为线程共享进程的资源比如堆和全局变量,多线程编程最重要的是,使用各种锁进行线程同步. 线程编程首先要学习的三个函数如下: #include &l ...

  10. Golang 读写锁RWMutex 互斥锁Mutex 源码详解

    前言 Golang中有两种类型的锁,Mutex (互斥锁)和RWMutex(读写锁)对于这两种锁的使用这里就不多说了,本文主要侧重于从源码的角度分析这两种锁的具体实现. 引子问题 我一般喜欢带着问题去 ...

随机推荐

  1. QQ/微信域名防红方法,打开网站跳转浏览器打开

    简单通用QQ/微信跳转浏览器打开代码 使用方法: 将代码全部复制 粘贴到 网站根目录下index.php文件的顶端 注意:不要覆盖了 index.php里面的原代码,原代码保留(请尽快把样式以及图片本 ...

  2. Educational Codeforces Round 175 (Rated for Div. 2) 比赛记录

    Educational Codeforces Round 175 (Rated for Div. 2) 比赛记录 比赛连接 手速场,上蓝场,但是有点唐,C 想错了写了半个多小时,想到正解不到 \(10 ...

  3. 【由技及道】镜像圣殿建造指南:Harbor私有仓库的量子封装艺术【人工智障AI2077的开发日志009】

    摘要:当容器镜像需要同时存在于8个平行宇宙时,就像在量子计算机里管理72个维度的镜像分身.本文记录一个未来AI如何通过Harbor搭建量子镜像圣殿,让容器分发成为跨越时空的瞬间传送. 动机:镜像管理的 ...

  4. echarts柱形图给X轴坐标类目添加点击事件

    在项目中遇到这么个需求要在柱形图上的x轴添加点击事件,当点击对应x轴文字的时候要弹出模态框展示子图表 根据echarts的Api给图表实例绑定点击事件 myChartInstance?.on('cli ...

  5. k8s Error: failed to prepare subPath for volumeMount "custom-logo" of container "grafana"

    前言 使用 k8s 挂载卷文件时,使用了 hostPath,type: File volumeMounts: - mountPath: /usr/share/grafana/public/img/gr ...

  6. 连接MySQL数据库出现时Authentication plugin 'caching_sha2_password' cannot be loaded的解决办法

    问题描述:用Navicat Premium或HeidiSQL连接MySQL数据库时会弹出下面的情况 解决方法: 1.运行命令行窗口,输入以下命令,输入密码后进入到mysql中,(最好将MySQL安装目 ...

  7. Oracle中IMP导入数据时提示字符集不一致解决

     生产环境中经常使用到Oracle的IMP导入和EXP导出来功能来达到数据迁移的目的,通常在源数据库和目标数据库中查询字符集是否致, 测试环境中导入IMP导入报错信息如下: 导入命令如下: [orac ...

  8. 导入SpaceClaim的iges模型尺寸被放大1000倍的问题

    问题 ANSYS APDL 和 Workbench 联合仿真时,导入 SpaceClaim 的 .iges 模型尺寸被放大 1000 倍数. 如 APDL 生成的尺寸为 10 mm(注:此处的 mm ...

  9. MySQL-SQL调优-引擎选错索引或者不使用索引分析 和 字符串加索引的方式思考

    优化器生成最优执行计划需要考虑的因素 MySQL有一个优化器,专门负责生成最优的查询计划,生成最优查询计划可能考虑的因素有: 扫描行数 是否排序 是否需要回表 是否需要临时表 等等 在不同的因素作用下 ...

  10. 继承中成员变量和成员方法的访问特点-java se进阶篇 day01

    1.继承中成员变量的访问特点 1.成员变量重名 如图 父类中有age变量,子类中也有age变量,这时打印age,出现的是10还是20呢? 答:根据就近原则,出现的是20 2.使用父类成员变量--sup ...