Redis-Redis多级缓存架构(实践)

分布式锁redisson的使用(并发场景下)

1.基于缓存,对热点数据进行刷新过期时间,以实现“冷热数据分离”

2.可以对“热点数据进行缓存重建”(双层获取)

3.使用分布式读写锁,可解决“数据库与缓存双写不一致”的场景

4.分布式读写锁的机制(读读共享,读写互斥,写写互斥)读写锁底层实现的是读写操作都是抢同一把锁,已控制拿锁的顺序。

        若是读读操作,则底层实现的是可重入锁机制,在进行读读操作是,多个线程拿的都是同一把读锁,通过对信号量state进行+1,来计算重入的次数及释放锁的量。

        若是写写,或写读场景,则其他未获取到锁的线程,则会处于等待阻塞状态。

@Service
public class ProductService {@Autowiredprivate ProductDao productDao;@Autowiredprivate RedisUtil redisUtil;@Autowiredprivate Redisson redisson;public static final Integer PRODUCT_CACHE_TIMEOUT = 60 * 60 * 24;public static final String EMPTY_CACHE = "{}";public static final String LOCK_PRODUCT_HOT_CACHE_PREFIX = "lock:product:hot_cache:";public static final String LOCK_PRODUCT_UPDATE_PREFIX = "lock:product:update:";public static Map<String, Product> productMap = new ConcurrentHashMap<>();@Transactionalpublic Product create(Product product) {Product productResult = productDao.create(product);redisUtil.set(RedisKeyPrefixConst.PRODUCT_CACHE + productResult.getId(), JSON.toJSONString(productResult),genProductCacheTimeout(), TimeUnit.SECONDS);return productResult;}@Transactionalpublic Product update(Product product) {Product productResult = null;//RLock updateProductLock = redisson.getLock(LOCK_PRODUCT_UPDATE_PREFIX + product.getId());RReadWriteLock readWriteLock = redisson.getReadWriteLock(LOCK_PRODUCT_UPDATE_PREFIX + product.getId());//通过读写锁来控制并发(读写锁 共用了是同一把锁)RLock writeLock = readWriteLock.writeLock();writeLock.lock();//这种不传时间值,当在并发条件下,那些未获取到锁的线程,会在一直处于阻塞状态,等待持有锁的线程执行完毕,再去唤醒需要持有锁的线程// writeLock.lock(10,TimeUnit.SECONDS);// 这种传入时间值,当在并发条件下,那些未获取到锁的线程,会在在10s时间内阻塞等待,若超过10s还未获取到锁,那么那些需要获取到锁的线程会重新再次去请求获取锁对象try {productResult = productDao.update(product);redisUtil.set(RedisKeyPrefixConst.PRODUCT_CACHE + productResult.getId(), JSON.toJSONString(productResult),genProductCacheTimeout(), TimeUnit.SECONDS); //更新缓存productMap.put(RedisKeyPrefixConst.PRODUCT_CACHE + productResult.getId(), product);//使用map去存储高热点数据(高热点数据比较少,则需要的内存较少,所以本地缓存可以使用),本地缓存可支持百万级别的并发,那么就服务就不会那么容易崩亏} finally {writeLock.unlock();}return productResult;}public Product get(Long productId) throws InterruptedException {Product product = null;String productCacheKey = RedisKeyPrefixConst.PRODUCT_CACHE + productId;//先从本地缓存中获取数据product = getProductFromCache(productCacheKey);if (product != null) {return product;}//DCL//若本地缓存中不存在,则通过分布式锁,进行加锁RLock hotCacheLock = redisson.getLock(LOCK_PRODUCT_HOT_CACHE_PREFIX + productId);//并发条件下,未获取到锁的线程,在此等待hotCacheLock.lock();//boolean result = hotCacheLock.tryLock(3, TimeUnit.SECONDS);try {//当加上分布式锁后再次中本地缓存中去获取数据,此为“热点缓存并发重建”//加分布式锁后,再次从本地缓存中去获取数据(考虑到,再加分布式锁过程中,有其他线程已经执行完查询的数据,并进行了缓存)product = getProductFromCache(productCacheKey);if (product != null) {return product;}//若缓存中未获取到数据,则通过redisson获取一把读写锁,来控制读写顺序(读写锁是共同抢同一把锁,具有互斥性,从而达到顺序执行)//同时分布式的读写锁,可以解决“数据库与缓存双写不一致”的问题//RLock updateProductLock = redisson.getLock(LOCK_PRODUCT_UPDATE_PREFIX + productId);RReadWriteLock readWriteLock = redisson.getReadWriteLock(LOCK_PRODUCT_UPDATE_PREFIX + productId);RLock rLock = readWriteLock.readLock();rLock.lock();try {product = productDao.get(productId);//读取数据库数据if (product != null) {redisUtil.set(productCacheKey, JSON.toJSONString(product),genProductCacheTimeout(), TimeUnit.SECONDS); //将取到的数据,进行redis缓存 和 本地缓存productMap.put(productCacheKey, product);} else {//若数据库中并没有该数据,则将缓存一个空对象,以防止恶意攻击redisUtil.set(productCacheKey, EMPTY_CACHE, genEmptyCacheTimeout(), TimeUnit.SECONDS);}} finally {rLock.unlock();//释放读锁}} finally {hotCacheLock.unlock(); //释放分布式锁}return product;}private Integer genProductCacheTimeout() {return PRODUCT_CACHE_TIMEOUT + new Random().nextInt(5) * 60 * 60;//设置不同的缓存过期时间,防止所有的key在同一时刻失效,造成缓存击穿,从而使服务瘫痪}private Integer genEmptyCacheTimeout() {return 60 + new Random().nextInt(30);//对空对象也进行设置不同的过期时间}private Product getProductFromCache(String productCacheKey) {Product product = productMap.get(productCacheKey);//先从本地缓存中获取数据,以提高数据访问量和有效性if (product != null) {return product;}String productStr = redisUtil.get(productCacheKey);//若本地缓存不存在,则从reids缓存中获取,以减少磁盘IOif (!StringUtils.isEmpty(productStr)) {  //判断需要获取的对象是否为空,若为空,则再次判断是否为空对象,若是空对象,则进行缓存if (EMPTY_CACHE.equals(productStr)) { //对获取的对象进行判断,是否为空对象(空对象存在的意义是防止恶意攻击,黑客攻击等),若为空对象,则返回一个新建的对象redisUtil.expire(productCacheKey, genEmptyCacheTimeout(), TimeUnit.SECONDS);return new Product();}product = JSON.parseObject(productStr, Product.class);redisUtil.expire(productCacheKey, genProductCacheTimeout(), TimeUnit.SECONDS);//读延期,已实现“冷热数据分离”(原理是当前被访问的对象,每被查询一次,就刷新一次过期时间)}return product;}}

对于缓存架构基于热点爆发数据处理:

1.当热点数据突然迸发时,容易出现“缓存雪崩”场景,则可通过前端进行限流处理。

2.前端有处理失效的情况,那么可考虑使用多级缓存架构,如:前端缓存,本地缓存,redis缓存

3.可对某些不重要的服务可进行做熔断降级处理。

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

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

相关文章

笔记二十、使用路由Params进行传递参数

20.1、在父组件中设置路由参数 <NavLink to{classify/${this.state.name}} className{this.activeStyle}>classify</NavLink> 父组件 Home/index.jsx import React from "react"; import {NavLink, Outlet} from "react-router-dom";class Ap…

2021年全国a级景区数据,shp+csv数据均有

大家好~这周将和大家分享关于文化旅游和城乡建设相关的数据&#xff0c;希望大家喜欢~ 今天分享的是2021年全国a级景区数据&#xff0c;数据格式有shpcsv&#xff0c;几何类型为点&#xff0c;已经经过清洗加工&#xff0c;可直接使用&#xff0c;以下为部分字段列表&#xff…

Linux中fork的进一步加深及信号基础

1.通过题目理解fork 1.打印结果?产生了几个进程? #include <stdio.h> #include <unistd.h> #include <stdlib.h> int main() { int i0; for(;i<2;i) { fork(); printf("A\n"); } exit(0); } 所以打印…

西南科技大学电路分析基础实验A1(元件伏安特性测试 )

目录 一、实验目的 二、实验设备 三、预习内容(如:基本原理、电路图、计算值等) 1、测定线性电阻的伏安特性 2、二极管伏安特性测试 3、测定实际电压源的伏安特性 四、实验数据及结果分析(预习写必要实验步骤和表格) 1、测定线性电阻的伏安特性 2、二极管伏安特性测…

Linux环境配置Seata开机自启脚本(在MySQL和Nacos启动后启动)

之前给seata配置了一个开机启动脚本&#xff0c;但是经常出现启动失败&#xff0c;查询日志要么MySQL没有连接上要么nacos连接不上&#xff0c;原因都是因为服务器重启的时候这两个服务都还没有完全启动&#xff0c;所以正常的做法应该是启动前先等前置服务启动好了再启动seata…

大语言模型:以Amazon Titan等大语言模型为例介绍

大语言模型&#xff08;Large Language Model&#xff09;是一种人工智能技术&#xff0c;通过对海量文本数据进行训练&#xff0c;学习语言的结构、规则和语义&#xff0c;从而可以生成具有自然语言风格的文本或回答自然语言的问题。大语言模型一般基于神经网络技术&#xff0…

【实时渲染】阅读Real-Time-Rendering-4th-CN的笔记整理(慢慢完善)

目录 相关链接图形渲染管线 相关链接 相关链接 B站视频&#xff1a;https://www.bilibili.com/video/BV1UM411Z7g1 PDF文件&#xff1a;https://github.com/Morakito/Real-Time-Rendering-4th-CN/releases/tag/v1.0 源文件仓库&#xff1a;https://github.com/Morakito/Real-T…

华为云CDN刷新与查询余量的Go实现及在Jenkins中的部署

引言 在华为云上&#xff0c;对CDN缓存内容进行刷新是一个常见的需求&#xff0c;以确保最新的内容能尽快被用户访问到。通过使用Go语言&#xff0c;我们可以开发一个自动化的工具来实现这一需求&#xff0c;并将其集成到Jenkins中以实现持续部署。下面我们将分步骤讲解如何实…

Bypass open_basedir的方法

文章目录 open_basedir概念绕过方法命令执行绕过symlink 绕过 &#xff08;软连接&#xff09;利用chdir()与ini_set()组合绕过 例题 [suctf 2019]easyweb open_basedir概念 open_basedir是php.ini的设置 在open_basedir设置路径的话 那么网站访问的时候 无法访问除了设置以外的…

KaiwuDB 亮相中国 5G + 工业互联网大会,助力新型工业化

11月19-21日&#xff0c;由各相关政府部门共同主办的“2023 中国 5G工业互联网大会”在湖北武汉盛大举行。作为我国“5G工业互联网”领域的国家级顶会&#xff0c;本届大会以“数实融合&#xff0c;大力推进新型工业化”为主题&#xff0c;聚焦新型基础设施、产业转型升级、技术…

redis—— 渐进式遍历

目录 为啥不用keys*遍历&#xff1f; 引入渐进式遍历 SCAN进行渐进式遍历 格式及参数说明 使用示例 注意 为啥不用keys*遍历&#xff1f; 之前学过key* 获取所有的key&#xff0c;但是这个操作可能会一次性得到太多的key&#xff0c;阻塞redis服务器&#xff0c;所以不建议…

笔记62:注意力汇聚 --- Nadaraya_Watson 核回归

本地笔记地址&#xff1a;D:\work_file\&#xff08;4&#xff09;DeepLearning_Learning\03_个人笔记\3.循环神经网络\第10章&#xff1a;动手学深度学习~注意力机制 a a a a a a a a a a a a a a a a

【一维数组】交换数组

题目 将数组A中的内容和数组B中的内容进行交换。&#xff08;数组一样大&#xff09; 解题方式通过函数封装可以实现任意类型的数组元素交换 思路来源&#xff1a;qsort函数的模拟实现 void Change_arr2(void* ch1, void* ch2, size_t num, size_t sz) {for (int i 0; i < …

.net core 事务

在 .NET Core 中&#xff0c;可以使用 Entity Framework Core 来实现事务处理。下面是一个简单的示例&#xff0c;展示了如何在 .NET Core 中使用 Entity Framework Core 来创建和执行事务&#xff1a; using System; using Microsoft.EntityFrameworkCore; using System.Tran…

Docker配置Halo搭建个人博客-快速入门

Docker配置Halo搭建个人博客-快速入门 1 官方文档2 安装Halo2.1 创建Halo主目录2.2 远程下载配置文件2.3 编辑配置文件2.4 拉取最新镜像2.6 查看容器2.7 开放服务器的防火墙 3 运行3.1 运行项目3.2 停止项目 4 常见问题4.1 没有权限4.2 ommand netstart not found, did you mea…

寻找两个正序数组的中位数

给定两个大小分别为 m 和 n 的正序&#xff08;从小到大&#xff09;数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。 算法的时间复杂度应该为 O(log (mn)) 。 &#xff1a;本题难点在于时间复杂度的掌握&#xff0c; 思路&#xff1a;把两个数组合为一个数…

如何在自己的github仓库建立xv6-labs-2020项目

由于github上没有放出xv6-labs-2020的源代码版本&#xff0c;所以如果想在github上建立自己关于这个实验的仓库&#xff0c;可以遵循下面步骤&#xff1a; 首先在某一个地方clone下源代码 git clone git://g.csail.mit.edu/xv6-labs-2020然后进入该文件夹运行git bash或者其他…

8.0 泛型

通过之前的学习&#xff0c;读者可以了解到&#xff0c;把一个对象存入集合后&#xff0c;再次取出该对象时&#xff0c;该对象的编译类型就变成了Object类型&#xff08;尽管其在运行时类型没有改变&#xff09;。集合设计成这样&#xff0c;提高了它的通用性&#xff0c;但是…

JAVA Spring boot Process finished with exit code 0

JAVA Spring boot Process finished with exit code 0 原因&#xff1a; 检查配置文件是否引入spring-boot-starter-web配置依赖 问题解决: 1.maven: pom引入 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-st…

Git的原理与使用(一):Git的基本操作(包含:版本回退)

Git原理与使用一 一.Git的初识与安装1.什么是Git2.如何安装Git1.git命令与git help(Git下的"man手册")2.centos下安装Git3.ubantu下安装Git 二.Git的前置操作与前置知识1.创建Git本地仓库2.配置Git3.理解Git的分区1.工作区2.暂存区3.版本库4.分区关系总结 三.添加文件…