【Redis】黑馬點評筆記:使用redis解決各種分布式/并發問題

1、系統架構

2、基于session登錄

用戶的 session 是由服務器(如 Tomcat)自動管理和維護的,每個用戶在訪問 Web 應用時都會擁有一個獨立的 session 對象。這個對象是通過瀏覽器和服務器之間的 HTTP 協議自動綁定的。


1. 如何區分不同用戶的 Session?
每個用戶的 session 是通過 Cookie 中的 JSESSIONID 來區分的。
當用戶第一次訪問服務器時,服務器會創建一個唯一的 HttpSession 對象,并生成一個唯一的標識符 JSESSIONID。
這個 JSESSIONID 會被寫入到客戶端的 Cookie 中。
后續每次請求中,客戶端會將 JSESSIONID 發送到服務器端,服務器根據這個 ID 找到對應的 HttpSession 實例。


示例流程:

  • 用戶 A 第一次請求 /user/code:
    • 服務器創建 HttpSession 實例 A,并分配 JSESSIONID=abc123。
    • 將 JSESSIONID=abc123 寫入用戶 A 的 Cookie。
  • 用戶 B 第一次請求 /user/code:
    • 服務器創建另一個 HttpSession 實例 B,并分配 JSESSIONID=xyz456。
    • 將 JSESSIONID=xyz456 寫入用戶 B 的 Cookie。
  • 用戶 A 再次請求 /user/code:
  • 瀏覽器自動攜帶 Cookie 中的 JSESSIONID=abc123。
  • 服務器找到對應的 HttpSession 實例 A 并使用它處理請求。

如下登錄代碼,校驗驗證碼:

通過取出該用戶對應session中保存的驗證碼和用戶傳遞的驗證碼對比進行校驗。其中session中的驗證碼實在給用戶發送短信的同時保存在session中的。

校驗通過,登錄成功。服務器將用戶信息存儲在該session中。之后每次用戶請求,將會通過cookie辨認對應的session,然后得知該用戶已登錄。

攔截器實現登錄校驗

集群session共享問題

Redis解決集群的session問題

一半選擇使用hashmap來存儲用戶信息(因為用戶信息往往是一個對象)

登錄邏輯

校驗邏輯

前端每次請求會在請求頭攜帶token

攔截器的使用

3、緩存

是什么?

緩存作用模型

緩存更新策略

主動更新策略

一般選擇方案1。后兩個方案都不成熟

先刪除緩存還是先操作數據庫?

方案二發生概率更低,因為查詢數據庫之后寫入緩存之間間隔往往很小(微秒級別);而更新數據庫并更新所需要的時間比較長。因而方案二發生數據不一致的概率比較小。

小結

緩存穿透

布隆過濾器里面就是一些二進制位。數據庫中的數據通過哈希算法映射到布隆過濾器中的某個位置上。

方法一:緩存空對象

緩存穿透的解決方案有哪些?

緩存雪崩

如果大量緩存的key的過期時間一樣,會導致他們同時失效;解決方案為下圖第一種。

緩存擊穿

解決方案

互斥鎖解決

使用redis的setnx命令實現邏輯上的自定義互斥鎖。

為了避免獲取鎖的進程出現故障而導致鎖無法被該進程釋放,一般還會為這個鎖設置有效期。

redis實現互斥鎖

邏輯過期解決

使用這種方法需要進行緩存預熱,也就是提前將熱點信息加入緩存當中。

redis里面設置邏輯過期字段可以參考以下代碼:

封裝緩存工具類

@Slf4j
@Component
public class CacheClient {private final StringRedisTemplate stringRedisTemplate;public CacheClient(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;}//普通插入redispublic void set(String key, Object value, Long time, TimeUnit unit){stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(value),time,unit);}//帶邏輯過期時間插入redispublic void setWithLogicalExpire(String key,Object value,Long time,TimeUnit unit){//設置邏輯過期RedisData redisData = new RedisData();redisData.setData(value);redisData.setExpireTime(LocalDateTime.now().plusSeconds(unit.toSeconds(time)));//寫入redisstringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(redisData));}//解決緩存穿透的查詢(返回空值)public <R,ID> R queryWithPassThrough(String keyPrefix, ID id, Class<R> type, Function<ID,R> dbFallback,Long time,TimeUnit unit){String key=keyPrefix+id;//1.嘗試從Redis查詢商鋪緩存String json = stringRedisTemplate.opsForValue().get(key);//2.判斷緩存是否存在if(StrUtil.isNotBlank(json)) { //判斷字符串既不為null,也不是空字符串(""),且也不是空白字符//3.存在,返回商鋪信息return JSONUtil.toBean(json, type);}//判斷是否為空值if(json!=null){return null;}//4.不存在,根據id查詢數據庫R r = dbFallback.apply(id);//5.判斷數據庫中是否存在if(r==null){//6.不存在,返回錯誤狀態碼stringRedisTemplate.opsForValue().set(key,"",RedisConstants.CACHE_NULL_TTL,TimeUnit.MINUTES);return null;}//7.存在,寫入redis,返回商鋪信息this.set(key,r,time,unit);return r;}private static final ExecutorService CACHE_REBUILD_EXECUTOR= Executors.newFixedThreadPool(10);//解決緩存擊穿的查詢(邏輯過期)public <R,ID> R queryWithLogicalExpire(String keyPrefix,ID id,Class<R> type,Function<ID,R> dbFallback,Long time,TimeUnit unit){String key=keyPrefix+id;//1.嘗試從Redis查詢商鋪緩存String json = stringRedisTemplate.opsForValue().get(key);//2.判斷緩存是否存在if(StrUtil.isBlank(json)) { //判斷字符串既不為null,也不是空字符串(""),且也不是空白字符//3.不存在,返回商鋪信息return null;}//4.存在,將json反序列化為對象RedisData redisData = JSONUtil.toBean(json, RedisData.class);R shop = JSONUtil.toBean((JSONObject) redisData.getData(),type);LocalDateTime expireTime = redisData.getExpireTime();//5.判斷是否過期if(expireTime.isAfter(LocalDateTime.now())) {//5.1.未過期,直接返回店鋪信息return shop;}//5.2.已過期,需要返回緩存重建//6.緩存重建//6.1.獲取互斥鎖String lockKey=RedisConstants.LOCK_SHOP_KEY+id;boolean isLock = tryLock(lockKey);//6.2.判斷是否獲取鎖成功if(isLock){//  6.3.成功,開啟獨立線程實現緩存重建CACHE_REBUILD_EXECUTOR.submit(()->{try {//查詢數據庫R r1= dbFallback.apply(id);//寫入redisthis.setWithLogicalExpire(key,r1,time,unit);} catch (Exception e) {throw new RuntimeException(e);}finally {//釋放鎖unLock(key);}});}//6.4.返回過期的商鋪信息return shop;}/*** 創建鎖* @param key* @return*/private boolean tryLock(String key){Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);return BooleanUtil.isTrue(flag);}/*** 封閉鎖* @param key*/private void unLock(String key){stringRedisTemplate.delete(key);}
}

4、JMeter-模擬高并發的工具

5、全局唯一ID

要求遞增性是為了數據庫查詢的高效。

6、秒殺優惠券

業務流程

超賣問題

解決辦法

其中使用悲觀鎖解決會導致性能嚴重下降。

樂觀鎖-版本號法

由于stock肯定是遞減的,因此將它作為“版本號”不會導致ABA問題的發生。就有如下解法:

一人一單業務

7、集群模式下的秒殺-分布式鎖

動機

分布式鎖

redis實現分布式鎖

使用set命令同時設置互斥的(NX)key和它的過期時間

誤刪別人鎖的問題

解決辦法

key對應value標識當前獲取鎖的對象

Lua腳本解決多條命令原子性問題

極端情況

假如某線程在剛剛判斷完當前鎖是不是自己的鎖之后,線程發生堵塞,則該線程在被喚醒之后還是有可能誤刪其他線程的鎖。

->要保證判斷鎖和釋放鎖這兩個動作共同形成一個原子操作。

Redis的Lua腳本

8、Redisson

事實上分布式鎖無需自己實現,會使用框架即可,如Redisson

快速入門

Redisson可重入鎖的原理

獲取鎖

釋放鎖

整體流程

Resson的multiLock

9、redis優化秒殺

redis判斷秒殺資格

優化后,下單的核心流程僅僅是以下:可見變得非常短且執行速度會非常快

異步下單-阻塞隊列

10、異步下單-消息隊列

redis消息隊列

redis消息隊列-list

下圖缺點“無法避免消息丟失”,因為有可能剛剛pop出來還沒處理就宕機了。

redis消息隊列-PubSub

“不支持數據持久化”——發布者發布消息的瞬間如果沒有任何訂閱者,這條消息就會消失。

“消息堆積有上限”——訂閱者有一個消息緩存區,存放收到了但是還沒來得及處理的消息。

redis消息隊列-Stream

寫消息

讀消息

“消息可回溯”——消息永遠會保存在隊列中

消費者組-基于stream

一個消費者組里面,消息被任意一個消費者讀取過就算被消費了。

小結對比

11、點贊功能

以博客id為key,點贊用戶的set集合為value存儲。

點贊排行榜

顯示最新點贊的n個用戶。使用SortedSet,score值設置為時間戳。

12、共同關注

以某個用戶為key,該用戶關注的所有用戶的set為value,存到redis。

共同關注取交集即可。

但是關注信息在mysql數據庫中也有一個表維護。(樹樹:不然怎么查看粉絲數……)

13、關注推送-feed流

拉模式

只有趙六準備讀取的時候才從他關注的up主的發件箱讀取消息——延時高

推模式

up主不保存信息,他一旦發布就將信息保存在所有粉絲的收信箱——保存多個副本,如有大v有幾千萬粉絲,則內存消耗巨大

推拉結合

新博客推送給粉絲-推模式

用戶發送博客,查詢粉絲列表,將新博客id存到所有粉絲的收件箱。

滾動分頁

【豆包】

滾動分頁(Scroll Pagination)是一種基于用戶滾動行為的分頁加載方式,常見于移動端應用、網頁信息流等場景,核心特點是當用戶滾動到頁面底部時,自動加載下一頁數據,無需手動點擊頁碼或 “加載更多” 按鈕,從而實現 “無限滾動” 的連續瀏覽體驗。

與傳統分頁的區別:

傳統分頁(如頁碼分頁)需要用戶通過點擊 “上一頁 / 下一頁” 或直接輸入頁碼跳轉,每次加載固定頁數的數據(如第 1 頁 10 條、第 2 頁 10 條),依賴 “頁碼” 作為分頁標識。

而滾動分頁不依賴頁碼,而是通過記錄 “上一頁最后一條數據的標記”(如 ID、時間戳、游標等)作為下一頁的查詢起點,動態加載后續內容。例如:

  • 首次加載第 1-10 條數據,記錄第 10 條的 ID 為lastId=10
  • 用戶滾動到底部時,自動請求lastId=10之后的 11-20 條數據,再記錄第 20 條的 ID 為lastId=20,以此類推。

傳統分頁的弊端

如果在查詢的同時有人插入了數據,就會發生重復讀取情況。

使用滾動分頁,每次記住上一次查詢到哪一條數據,下次查詢從這條數據往后查。

實現方法:

每次從用戶郵箱讀取的時候,記住max,并計算出offset,傳遞給前端。前端下次查詢會攜帶這兩個數據。

優化點?

b站彈幕:我覺得可以用戶在線推到redis,不在線就不推了,用戶在線下拉刷新是去讀redis,向上滑讀mysql

樹樹:這么做是因為會導致redis內存壓力太大?我感覺可以這樣:用戶上線的時候/刷新的時候將數據庫所有關注的人的筆記加載到redis(當然還要設置一個過期時間);然后后面再……

14、附近商鋪

GEO-地理坐標

添加坐標點

計算兩個點之間的距離

計算一個圓內所有點,圓心是給定的

上面這些命令中,g1是key,代表一個存放了若干個地理坐標點的數據集。

實現

不同類型的商鋪分開存。redis存的只有商戶id和對應經緯度,而沒有全部具體的商鋪信息。

15、用戶簽到-BitMap

實現

位運算遍歷每個bit位。

16、UV統計

UV、PV是什么?

HyperLogLog

重復插入的元素只記錄一次

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

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

相關文章

Javaweb- 11 MVC架構模式

MVC&#xff08;Model View Controller&#xff09; 是軟件工程中一種軟件架構模式&#xff0c;它把軟件系統分為模型&#xff0c;視圖&#xff0c;控制器&#xff0c;三個基本部分。用一種業務邏輯&#xff0c;數據&#xff0c;界面顯示分離的方法組織代碼&#xff0c;將業務邏…

【電腦】主板的基礎知識

主板&#xff08;Motherboard&#xff09;是計算機的核心組件之一&#xff0c;它將所有其他硬件部件連接在一起并協調它們的工作。以下是關于主板的詳細知識&#xff1a;1. 架構組成一個典型的主板通常由以下幾個主要部分構成&#xff1a;芯片組&#xff08;Chipset&#xff09…

【飛算JavaAI】一站式智能開發,驅動Java開發全流程革新

【作者主頁】Francek Chen 【專欄介紹】???人工智能與大模型應用??? 人工智能&#xff08;AI&#xff09;通過算法模擬人類智能&#xff0c;利用機器學習、深度學習等技術驅動醫療、金融等領域的智能化。大模型是千億參數的深度神經網絡&#xff08;如ChatGPT&#xff09…

STM32中的RTC(實時時鐘)詳解

前言&#xff1a;為什么需要RTC&#xff1f; 在嵌入式系統中&#xff0c;時間記錄是一項基礎且關鍵的功能。想象一下&#xff1a;智能家居設備需要按時間觸發開關燈&#xff0c;工業儀表需要記錄傳感器數據的采集時刻&#xff0c;物聯網終端需要同步服務器時間戳……這些場景都…

Python技巧記錄

空格拼接數組格式化顯示 一維數組 arr [1, 2, 3, 4, 5] print( .join(map(str, arr))) # 直接轉換并連接二維數組 for row in arr:print( .join(map(str, row)))for row in arr: 此循環會遍歷矩陣arr中的每一行。這里的arr是一個二維列表&#xff0c;每一行代表一個子列表。m…

next.js打包后的前端資源如何進行部署和訪問,為什么沒有index.html

在 Next.js 項目中&#xff0c;打包后的部署方式和傳統單頁應用&#xff08;SPA&#xff09;有所不同&#xff0c;尤其是沒有直接生成 index.html 這一點。以下是詳細解釋和部署指南&#xff1a;為什么沒有 index.html 文件&#xff1f; Next.js 采用 混合渲染策略&#xff0c;…

Qt+FFmpeg網絡視頻流播放

init 函數用于初始化 FFmpeg&#xff0c;包括設置參數、打開輸入、初始化視頻和音頻等。initOption 函數用于設置 FFmpeg 的參數選項。bool FFmpegThread::init() {if (url.isEmpty()) {return false;}//判斷該攝像機是否能聯通if (checkConn && isRtsp) {if (!checkUr…

【SpringBoot】Spring Boot 高并發優化終極指南,涵蓋線程模型、JVM 調優、數據庫訪問、緩存策略等 15+ 核心模塊

Spring Boot 高并發優化終極指南&#xff0c;涵蓋線程模型、JVM 調優、數據庫訪問、緩存策略等 15 核心模塊一、線程模型深度調優&#xff08;核心瓶頸突破&#xff09;1. Tomcat 線程池原子級配置2. 異步任務線程池隔離策略二、JVM 層終極調參&#xff08;G1GC 深度優化&#…

linux(CentOS-7-x86_64:NAT模式下解決yum無法使用:更新yum源的詳細操作步驟2025)

目錄 一、CentOS-7-x86_64的NAT模式下解決yum無法使用。&#xff08;更新可用的yum&#xff09; &#xff08;1&#xff09;首先保證能夠ping通&#xff0c;也就是NAT模式下虛擬機有網絡。 &#xff08;2&#xff09;錯誤&#xff1a;無法使用yum。比如我現在無法yum search if…

C++11的整理筆記

Lambda 表達式Lambda 表達式是 C11 引入的一種強大的功能&#xff0c;它允許你在代碼中直接定義匿名函數對象。Lambda 表達式可以捕獲上下文中的變量&#xff0c;并在需要時使用它們。它們通常用于簡化代碼&#xff0c;尤其是那些需要傳遞函數對象作為參數的場景&#xff08;如…

MS1826+MS9332 4K@30Hz HD4×2視頻分割器

MS1826MS9332是一款支持4K30Hz分辨率的HD42視頻分割器方案。支持四路HD輸入兩路HD輸出&#xff0c;最高支持4K30Hz分辨率。該方案具有Scaler、OSD、畫面分割、無縫切換、淡入淡出及旋轉等功能。該方案現已實現量產&#xff0c;并提供完善的技術支持&#xff0c;適用于各類高清視…

用 MATLAB 模擬傳染病傳播:從 SI 模型到 SIS 模型的可視化之旅

在公共衛生研究中&#xff0c;數學模型是理解傳染病傳播規律的重要工具。通過數值模擬&#xff0c;我們能直觀看到 “易感人群” 和 “感染人群” 隨時間的變化趨勢&#xff0c;甚至能預測疫情發展的關鍵節點。今天就帶大家用 MATLAB 實現兩個經典的傳染病模型 ——SI 模型和SI…

Ruby如何采集直播數據源地址

在當今數字化的時代&#xff0c;實時獲取并處理信息變得尤為重要。特別是在體育賽事、新聞報道等領域&#xff0c;及時獲取最新的直播數據源對于提升用戶體驗至關重要。本文將介紹如何使用Ruby語言來采集特定網站的數據源地址 一、準備工作 首先&#xff0c;確保你的環境中已…

【fitz+PIL】PDF圖片文字顏色加深

文章目錄0 引言1 解決思路及流程1.1 思路1.2 代碼實現2 完整代碼與效果3 總結0 引言 沒錯&#xff0c;這是連續劇。女友對上一篇【fitzOpenCV】去除PDF圖片中的水印得到的去水印效果很滿意&#xff0c;于是問我可不可以再幫她處理一下另一個PDF文件&#xff0c;我二話不說答應…

tp8.0\jwt接口安全驗證

環境&#xff1a;php8.3\tp8.1\firebase-jwt6.1app\middleware\JwtAuth<?php namespace app\middleware;use app\common\library\JwtHandler; use think\Request; use think\facade\Env;class JwtAuth {public function handle(Request $request, \Closure $next){// 獲取當…

ReactNative【實戰系列教程】我的小紅書 5 -- 文章詳情(含輪播圖 ImageSlider,點亮紅心動畫 Heart,嵌套評論等)

最終效果 安裝依賴 npm i dayjs用于對時間進行格式化 必備組件 輪播圖 ImageSlider https://blog.csdn.net/weixin_41192489/article/details/149224510 點亮紅心動畫 Heart components/Heart.tsx import AntDesign from "expo/vector-icons/AntDesign"; import …

嗶哩嗶哩第三方TV-BBLL最新版

—————【下 載 地 址】——————— 【?本章下載一】&#xff1a;https://pan.xunlei.com/s/VOUtUcaymd9rpgurgDKS9pswA1?pwdp76n# 【?本章下載二】&#xff1a;https://pan.xunlei.com/s/VOUtUcaymd9rpgurgDKS9pswA1?pwdp76n# 【百款黑科技】&#xff1a;https://uc…

用YOLOv5系列教程(1)-用YOLOv5輕松實現設備狀態智能監控!工業級教程來了

用YOLOv5輕松實現設備狀態智能監控&#xff01;工業級教程來了設備運維革命&#xff1a;15分鐘教會你的攝像頭看懂指示燈狀態工業現場各種設備狀態指示燈是工程師的"眼睛"——紅燈報警、綠燈運行、黃燈待機。但人工巡檢耗時費力&#xff0c;關鍵故障容易漏檢&#xf…

筆記-分布式計算基礎

Distributed Computing 劃分數據并行&#xff08;DataParallelism&#xff09;將數據分為n份&#xff0c;發送到n個GPU上&#xff0c;每個GPU上都存在一個完整的大模型 缺點&#xff1a; 模型太大Pipeline Parallelism&#xff08;串行的&#xff09;將模型做split,每個GPU負責…

Android Studio 2024,小白入門喂飯級教程

一、下載Android Studio 1、從安卓官網安卓官網下載Android Studio。 ? ? 二、安裝Android Studio 1、雙擊android-studio-2024.3.2.15-windows.exe。 ? ?? ? ? ? ? 三、新建工程 1、雙擊桌面圖標? 打開Android Studio。 ? 選“Empty Views Activity…