gulimall-緩存-緩存使用

文章目錄

  • 前言
  • 一、本地緩存與分布式緩存
    • 1.1 使用緩存
    • 1.2 本地緩存
    • 1.3 本地模式在分布式下的問題
    • 1.4 分布式緩存
  • 二、整合redis測試
    • 2.1 引入依賴
    • 2.2 配置信息
    • 2.3 測試
  • 三、改造三級分類業務
    • 3.1 代碼改造
  • 四、高并發下緩存失效問題
    • 4.1 緩存穿透
    • 4.2 緩存雪崩
    • 4.3 緩存擊穿
  • 五、分布式下加鎖
    • 5.1 分布式鎖示意圖
    • 5.2 鎖的時序問題

前言

本文繼續記錄B站谷粒商城項目視頻 P151-157 的內容,做到知識點的梳理和總結的作用。

一、本地緩存與分布式緩存

1.1 使用緩存

為了系統性能的提升,我們一般都會將部分數據放入緩存中,加速訪問。而 db 承擔數據落盤工作。
哪些數據適合放入緩存?

  1. 即時性、數據一致性要求不高的
  2. 訪問量大且更新頻率不高的數據(讀多,寫少)
    舉例:電商類應用,商品分類,商品列表等適合緩存并加一個失效時間(根據數據更新頻率來定),后臺如果發布一個商品,買家需要 5 分鐘才能看到新的商品一般還是可以接受的。

在這里插入圖片描述
偽代碼

data = redisTemplate.opsForValue().get(redisKey);//從緩存加載數據
If(data == null){//緩存中沒有則從數據庫加載數據data = db.getDataFromDB(id);//保存到 cache 中redisTemplate.opsForValue().set(redisKey,data);
}
return data;

1.2 本地緩存

在單體項目中,我們可以使用 Map 集合存儲數據作為項目的本地緩存,因為 Map 數據是存儲與內存的,相比于數據庫查詢要從磁盤加載到內存有著更高的效率。

在這里插入圖片描述

1.3 本地模式在分布式下的問題

但是在分布式情況下這種情況就不再適用了,每個微服務可能部署在多臺機器上,每個機器上有各自的緩存 Map 對象,會導致數據不一致的問題。
在這里插入圖片描述

1.4 分布式緩存

所以應該將數據緩存在同一個緩存中間件中,才能保證數據一致性問題

在這里插入圖片描述

二、整合redis測試

2.1 引入依賴

<!-- 緩存中間件redis依賴-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2.2 配置信息

spring:redis:host: 192.168.57.129port: 6379

2.3 測試

@Autowired
StringRedisTemplate redisTemplate;@Test
public void testRedis() {//存儲redisTemplate.opsForValue().set("HELLO_REDIS", "SpringBoot!");//獲取String value = redisTemplate.opsForValue().get("HELLO_REDIS");System.out.println(value);
}

在這里插入圖片描述

三、改造三級分類業務

3.1 代碼改造

@Override
public Map<String, List<Catelog2Vo>> getCatalogJson() {//給緩存中放json字符串,拿出的json字符串,還用逆轉為能用的對象類型:【序列化與反序列化】/*** 1、空結果緩存:解決緩存穿透* 2、設置過期時間(加隨機值):解決緩存雪崩* 3、加鎖:解決緩存擊穿*///1、加入緩存邏輯,緩存中存的數據是json字符串。//JSON跨語言,跨平臺兼容。String catalogJSON = redisTemplate.opsForValue().get("catalogJSON");if (StringUtils.isEmpty(catalogJSON)) {//2、緩存中沒有,查詢數據庫//保證數據庫查詢完成以后,將數據放在redis中,這是一個原子操作。log.info("緩存不命中....將要查詢數據庫...");Map<String, List<Catelog2Vo>> catalogJsonFromDb = getCatalogJsonFromDB();String result = JSON.toJSONString(catalogJsonFromDb);redisTemplate.opsForValue().set("catalogJSON",result);}log.info("緩存命中....直接返回....");//轉為我們指定的對象。return JSON.parseObject(catalogJSON, new TypeReference<Map<String, List<Catelog2Vo>>>() {});
}

四、高并發下緩存失效問題

4.1 緩存穿透

在這里插入圖片描述
解決方案1:null 結果放入緩存,并加入短暫的過期時間

偽代碼

//從緩存加載數據
data = redisTemplate.opsForValue().get(redisKey);
If(data == null){//緩存中沒有則從數據庫加載數據data = db.getDataFromDB(id);if(data == null) {//空結果保存到 cache 中redisTemplate.opsForValue().set(redisKey,null,300,TimeUnit.SECONDS);}else {//保存到 cache 中redisTemplate.opsForValue().set(redisKey,data);}
}
return data;

解決方案2:使用布隆過濾器
這種技術在緩存之前再加一層屏障,里面存儲目前數據庫中存在的所有key。當業務系統有查詢請求的時候,首先去BloomFilter中查詢該key是否存在。若不存在,則說明數據庫中也不存在該數據,因此緩存都不要查了,直接返回null。若存在,則繼續執行后續的流程,先前往緩存中查詢,緩存中沒有的話再前往數據庫中的查詢。偽代碼如下:

String get(String key) {String value = redis.get(key);     if (value  == null) {if(!bloomfilter.mightContain(key)){//不存在則返回return null; }else{//可能存在則查數據庫value = db.get(key); redis.set(key, value); }    }return value;
}

布隆過濾器示意圖

在這里插入圖片描述

4.2 緩存雪崩

在這里插入圖片描述

4.3 緩存擊穿

在這里插入圖片描述

五、分布式下加鎖

5.1 分布式鎖示意圖

本地鎖,只能鎖住當前進程,所以我們需要分布式鎖。
在這里插入圖片描述

5.2 鎖的時序問題

在這里插入圖片描述

@Override
public Map<String, List<Catelog2Vo>> getCatalogJson() {//給緩存中放json字符串,拿出的json字符串,還用逆轉為能用的對象類型:【序列化與反序列化】/*** 1、空結果緩存:解決緩存穿透* 2、設置過期時間(加隨機值):解決緩存雪崩* 3、加鎖:解決緩存擊穿*///1、加入緩存邏輯,緩存中存的數據是json字符串。//JSON跨語言,跨平臺兼容。String catalogJSON = redisTemplate.opsForValue().get("catalogJSON");if (StringUtils.isEmpty(catalogJSON)) {//2、緩存中沒有,查詢數據庫//保證數據庫查詢完成以后,將數據放在redis中,這是一個原子操作。log.info("緩存不命中....將要查詢數據庫...");Map<String, List<Catelog2Vo>> catalogJsonFromDb = getCatalogJsonFromDB();return catalogJsonFromDb;}log.info("緩存命中....直接返回....");//轉為我們指定的對象。return JSON.parseObject(catalogJSON, new TypeReference<Map<String, List<Catelog2Vo>>>() {});
}

查詢數據庫后將結果放入緩存,保證這是一個原子性操作,防止多個線程查詢數據庫而導致日志輸出多個查詢了數據庫…

//從數據庫查詢并封裝分類數據
public Map<String, List<Catelog2Vo>> getCatalogJsonFromDB() {//只要同一把鎖,就能鎖住需要這個鎖的所有線程//synchronized (this):springBoot所有組件在容器中都是單實例的//TODO 本地鎖:synchronized JUC(Lock) 在分布式情況下只能使用分布式鎖才能鎖住資源synchronized (this) {//得到鎖以后,我們應該再去緩存中確定一次,如果沒有才需要繼續查詢String catalogJSON = redisTemplate.opsForValue().get("catalogJSON");if (!StringUtils.isEmpty(catalogJSON)) {return JSON.parseObject(catalogJSON, new TypeReference<Map<String, List<Catelog2Vo>>>() {});}log.info("查詢了數據庫....");//1、將數據庫的多次查詢變為一次,查詢所有分類信息List<CategoryEntity> selectList = baseMapper.selectList(null);//1、查出所有1級分類List<CategoryEntity> level1Categorys = getParent_cid(selectList, 0L);//2、封裝數據Map<String, List<Catelog2Vo>> parent_cid = level1Categorys.stream().collect(Collectors.toMap(k -> k.getCatId().toString(), v -> {//1、每一個的一級分類,查到這個一級分類的二級分類List<CategoryEntity> categoryEntities = getParent_cid(selectList, v.getCatId());//2、封裝上面的結果List<Catelog2Vo> catelog2Vos = null;if (categoryEntities != null) {catelog2Vos = categoryEntities.stream().map(l2 -> {Catelog2Vo catelog2Vo = new Catelog2Vo(v.getCatId().toString(), null, l2.getCatId().toString(), l2.getName());//1、找當前二級分類的三級分類封裝成voList<CategoryEntity> level3Catelog = getParent_cid(selectList, l2.getCatId());if (level3Catelog != null) {List<Catelog2Vo.Catelog3Vo> collect = level3Catelog.stream().map(l3 -> {//2、封裝成指定格式Catelog2Vo.Catelog3Vo catelog3Vo = new Catelog2Vo.Catelog3Vo(l2.getCatId().toString(), l3.getCatId().toString(), l3.getName());return catelog3Vo;}).collect(Collectors.toList());catelog2Vo.setCatalog3List(collect);}return catelog2Vo;}).collect(Collectors.toList());}return catelog2Vos;}));String result = JSON.toJSONString(parent_cid);redisTemplate.opsForValue().set("catalogJSON",result,1,TimeUnit.DAYS);return parent_cid;}
}

壓力測試結果:日志只輸出一個查詢了數據庫…,表面只有一個線程查詢了數據庫。
在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/40238.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/40238.shtml
英文地址,請注明出處:http://en.pswp.cn/news/40238.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

C++學習第十二天----指針

1.自動存儲&#xff0c;靜態存儲和動態存儲 c有3種管理數據內存的方式&#xff1a;自動存儲&#xff0c;靜態存儲和動態存儲&#xff08;有時也叫自由存儲空間或堆&#xff09;&#xff1b;C其實還有第4種類型----線程存儲&#xff1b; 第一&#xff0c;自動存儲&#xff0c;在…

Talk | ICCV‘23 HumanMAC:簡潔易拓展的人體動作預測新框架

? 本期為TechBeat人工智能社區第522期線上Talk&#xff01; 北京時間8月16日(周三)20:00&#xff0c;清華大學博士生—陳凌灝的Talk已準時在TechBeat人工智能社區開播&#xff01; 他與大家分享的主題是: “HumanMAC-簡潔易拓展的人體動作預測新框架”&#xff0c;介紹了人體動…

linux 學習————LNMP之分布式部署

目錄 一、概述 二、LNMP環境部署 三、配置nginx 四、 配置php使nginx能夠解析.php 五、配置mysql 六、配置discuz進行登錄論壇訪問測試 一、概述 LNMP代表 Linux、Nginx、MySQL、PHP&#xff0c;是一種常用的服務器架構。它由以下組件組成&#xff1a; Linux&#xff1a;作…

【js】js中apply()、bind()、call()用法

這三個方法的作用基本上相同&#xff0c;用法上有一些不同&#xff0c;下面先對比一下它們的用法&#xff1a; apply&#xff1a;調用一個具有給定 this 值的函數&#xff0c;以及以一個數組&#xff08;或一個類數組對象&#xff09;的形式提供的參數。 語法&#xff1a; ap…

Metasploitable2靶機漏洞復現

一、信息收集 nmap掃描靶機信息 二、弱口令 1.系統弱口令 在Kali Linux中使用telnet遠程連接靶機 輸入賬號密碼msfadmin即可登錄 2.MySQL弱口令 使用mysql -h 靶機IP地址即可連接 3.PostgreSQL弱密碼登錄 輸入psql -h 192.168.110.134 -U postgres 密碼為postgres 輸入\…

每天一個電商API分享:淘寶商品銷量接口,獲取淘寶商品銷售數據(淘寶商品精準銷量、總銷量、月銷量)

開發背景 商品銷量是商家決策的重要參考指標之一。通過淘寶商品銷量接口&#xff0c;商家可以獲取到實時的銷量信息&#xff0c;從而更好地進行庫存管理、供應鏈計劃和市場推廣等決策。 淘寶商品銷量接口可以提供豐富的銷售數據&#xff0c;包括銷售數量、銷售額、銷售渠道等…

unity中導入自定義模型

unity中導入自定義模型 準備軟件步驟1從SoildWorks中導出模型為STEP格式2將STEP格式文件導入到3DS Max中&#xff0c;再導出為FBX格式3將FBX格式導入至unity中 準備軟件 需要SoildWorks、3DS Max和Unity 3D軟件步驟 1從SoildWorks中導出模型為STEP格式 2將STEP格式文件導入到…

【數據結構】 List與順序表及接口的實現

文章目錄 什么是List常見接口介紹線性表順序表順序表接口的實現add在末尾新增元素在 pos 位置新增元素判定是否包含某個元素查找某個元素對應的位置獲取 pos 位置的元素給 pos 位置的元素設為 value刪除第一次出現的關鍵字key獲取順序表的長度清空順序表 順序表的優缺點優點&am…

全面掌握 Jaeger 分布式調用鏈路跟蹤理論和實戰,Go 為所有使用 go-resty 庫發起 HTTP 請求集成鏈路跟蹤 jaeger(附源碼)

全面掌握 Jaeger 分布式調用鏈路跟蹤理論和實戰,Go 為所有使用 go-resty 庫發起 HTTP 請求集成鏈路跟蹤 jaeger(附源碼)。 介紹一個開源的分布式跟蹤系統 Jaeger,首先從理論基礎知識開始學習,將學習如何在 HTTP 請求中集成鏈路跟蹤,以及如何在 GORM 框架實現,最后學習 …

Qt應用開發(基礎篇)——滾屏區域基類 QAbstractScrollArea

一、前言 QAbstractScrollArea滾屏區域抽象類繼承于QFrame&#xff0c;QFrame繼承于QWidget&#xff0c;是QListview(列表瀏覽器)、QTableview(表格瀏覽器)、QTextEdit(文本編輯器)、QTextBrowser(文本瀏覽器)等所有需要滾屏區域部件的抽象基類。 框架類QFrame介紹 QAbstractSc…

java -jar 啟動服務后,關閉命令窗口后服務停止

java -jar 啟動服務后&#xff0c;關閉命令窗口后服務停止 問題&#xff1a;當我們用java -jar命令啟動服務后&#xff0c;只有一直保持Xshell的窗口開啟且正常連接服務器時才能訪問服務&#xff0c;當關閉命令窗口時&#xff0c;服務會停止運行 解決&#xff1a;使用nohup命…

java之juc二

JMM 請你談談對Volatile的理解 Volatile是jvm提供的輕量級的同步機制&#xff08;和synchronized差不多&#xff0c;但是沒有synchronized那么強大&#xff09; 保證可見性不保證原子性禁止指令重排 什么是JMM JMM&#xff1a;java內存模型&#xff0c;不存在的東西&#…

常用Linux命令

常用Linux命令 1. 基本命令 uname -m 顯示機器的處理器架構 uname -r 顯示正在使用的內核版本 dmidecode -q 顯示硬件系統部件 (SMBIOS / DMI) hdparm -i /dev/hda 羅列一個磁盤的架構特性 hdparm -tT /dev/sda 在磁盤上執行測試性讀取操作系統信息 arch 顯示機器的處理器架構…

一.RocketMQ概念

RocketMQ概念 1.概念2.應用場景3.MQ的優點和缺點4.常見MQ對比 1.概念 MQ(Message Queue)&#xff0c;是一種提供消息隊列服務的中間件&#xff0c;也稱為消息中間件&#xff0c;是一套提供了消息生產、存儲、消費全過程API的軟件系統。 RocketMQ是阿里巴巴2016年MQ中間件&…

Error: Couldn‘t find preset “es2015“ relative to directory

vue引入element-ui&#xff0c;運行時報了這個錯誤 Module build failed: Error: Couldnt find preset "es2015" relative to directory "D:\\360MoveData\\Users\\Administrator\\Desktop\\新建文件夾\\henge-test"at D:\360MoveData\Users\Administrato…

華為云classroom賦能--Devstar使應用開發無需從零開始

華為云DevStar為開發者提供業界主流框架代碼初始化能力&#xff0c;通過GUI、API、CLI等多種方式&#xff0c;將按模板生成框架代碼的能力推送至用戶桌面。同時基于華為云服務資源、成熟的DevOps開發工具鏈和面向多場景的眾多開發模板&#xff0c;提供一站式創建代碼倉、自動生…

Windows server 2016如何安裝OpenSSH

在 Windows Server 2016 上安裝 OpenSSH 需要通過“添加功能和角色”向導來完成。以下是安裝 OpenSSH 的步驟&#xff1a; 1.打開 Windows Server 2016 控制面板。 2.點擊 "程序"&#xff0c;然后選擇 "程序和功能"。 3.在左側菜單中&#xff0c;點擊 &…

【golang】數組和切片底層原理

數組類型的值&#xff08;以下簡稱數組&#xff09;的長度是固定的&#xff0c;而切片類型的值&#xff08;以下簡稱切片&#xff09;是可變長的。 數組的長度在聲明它的時候就必須給定&#xff0c;并且之后不會再改變。可以說&#xff0c;數組的長度是其類型的一部分。比如&a…

Spring學習筆記之Spring IoC注解式開發

文章目錄 聲明Bean的注解Component注解Controller注解Service注解Repository Spring注解的使用選擇性實例化Bean負責注入的注解ValueAutowired與QuaifierResource 全注解式開發 注解的存在主要是為了簡化XML的配置。Spring6倡導全注解開發 注解怎么定義&#xff0c;注解中的屬性…

深入探索JavaEE單體架構、微服務架構與云原生架構

課程鏈接&#xff1a; 鏈接: https://pan.baidu.com/s/1xSI1ofwYXfqOchfwszCZnA?pwd4s99 提取碼: 4s99 復制這段內容后打開百度網盤手機App&#xff0c;操作更方便哦 --來自百度網盤超級會員v4的分享 課程介紹&#xff1a; &#x1f50d;【00】模塊零&#xff1a;開營直播&a…