【Redis】緩存和分布式鎖

?????🔥個人主頁:?中草藥

🔥專欄:【中間件】企業級中間件剖析


一、緩存(Cache)

概述

? ? ? ? Redis最主要的應用場景便是作為緩存。緩存(Cache)是一種用于存儲數據副本的技術或組件,目的是提高數據訪問性能、減輕后端數據源負載 。

數據存儲:在靠近數據源或用戶的位置,開辟一塊存儲空間,用于存放常用或熱點數據副本。例如瀏覽器緩存網頁資源(圖片、CSS、JavaScript 文件等),將其存儲在本地磁盤或內存特定區域?

訪問邏輯:當應用程序請求數據時,先檢查緩存中是否有所需數據。若存在(即緩存命中 ),直接從緩存讀取返回,避免對原始數據源(如數據庫、遠程服務器 )的訪問;若不存在(緩存未命中 ),則從原始數據源獲取數據,同時可將數據存入緩存,供后續可能的請求使用 。

在緩存設計中,二八定律體現得十分明顯

通常約 20% 的熱點數據會被頻繁訪問,占據約 80% 的訪問請求量 。

?使用Redis作為緩存

關系性數據庫有一個很大的缺陷,性能不高(進行一次查詢操作小號的系統資源比較多),主要原因有以下幾點:

1)數據庫把數據存儲在硬盤上,硬盤的 IO 速度并不快。尤其是隨機訪問.

2)如果查詢不能命中索引,就需要進行表的遍歷,這就會大大增加硬盤 IO 次數.

3)關系型數據庫對于 SQL 的執行會做一系列的解析,校驗,優化工作.

4)如果是一些復雜查詢,比如聯合查詢,需要進行笛卡爾積操作,效率更是降低很多.

因此,如果訪問數據庫的并發量比較高,對于數據庫的壓力比較大,很容易使數據庫服務器宕機,應對此的策略主要有兩種,實際開發中,這兩種方案往往是會搭配使用的.

開源:引入更多的機器,部署更多的數據庫實例,構成數據庫集群.(主從復制,分庫分表等...)

節流:引入緩存,使用其他的方式保存經常訪問的熱點數據,從而降低直接訪問數據庫的請求數量.

Redis是一個用來作為數據庫緩存的常用方案

Redis 訪問速度比 MySQL 快很多。或者說處理同一個訪問請求,Redis 消耗的系統資源比 MySQL 少很多。因此 Redis 能支持的并發量更大.

  • Redis 數據在內存中,訪問內存比硬盤快很多.
  • Redis 只是支持簡單的 key-value 存儲,不涉及復雜查詢的那么多限制規則.

緩存的更新策略

緩存的使用存在一個重要問題,那些數據屬于是熱點數據

1)定期生成

????????每隔一定的周期 (比如一天 / 一周 / 一個月), 對于訪問的數據頻次進行統計。挑選出訪問頻次最高的前 N% 的數據,可以通過定時任務來進行觸發

以搜索引擎為例.
????????用戶在搜索引擎中會輸入一個 "查詢詞", 有些詞是屬于高頻的,大家都愛搜,有些詞就屬于低頻的,大家很少搜.
????????搜索引擎的服務器會把哪個用戶什么時間搜了啥詞,都通過日志的方式記錄的明明白白。然后每隔一段時間對這期間的搜索結果進行統計 (日志的數量可能非常巨大,這個統計的過程可能需要使用 hadoop 或者 spark 等方式完成). 從而就可以得到 "高頻詞表".

優點:上述過程,實際上實現起來比較簡單的。過程更可控,方便排查問題.
缺點:實時性不夠。如果出現一些突發性事件,在短時間內,有一些本來不是熱詞的內容,成了熱詞了。新的熱詞就可能給后面的數據庫啥的帶來較大的壓力。

2)實時生成

先給緩存設定容量上限 (可以通過 Redis 配置文件的 maxmemory 參數設定).
接下來把用戶每次查詢:

如果在 Redis 查到了,就直接返回.

如果 Redis 中不存在,就從數據庫查,把查到的結果同時也寫入 Redis.

????????如果緩存已經滿了 (達到上限), 就觸發緩存淘汰策略,把一些 "相對不那么熱門" 的數據淘汰掉,按照上述過程,持續一段時間之后 Redis 內部的數據自然就是 "熱門數據" 了.

Redis緩存淘汰策略

通用的緩存淘汰策略有以下幾種:

FIFO (First In First Out) 先進先出
把緩存中存在時間最久的(也就是先來的數據)淘汰掉。

LRU (Least Recently Used) 淘汰最久未使用的
記錄每個 key 的最近訪問時間,把最近訪問時間最老的 key 淘汰掉。

LFU (Least Frequently Used) 淘汰訪問次數最少的
記錄每個 key 最近一段時間的訪問次數,把訪問次數最少的淘汰掉。

Random 隨機淘汰
從所有的 key 中抽取幸運兒被隨機淘汰掉。


Redis 內置的淘汰策略如下:

  • volatile-lru 當內存不足以容納新寫入數據時,從設置了過期時間的 key 中使用 LRU(最久未使用)算法進行淘汰

  • allkeys-lru 當內存不足以容納新寫入數據時,從所有 key 中使用 LRU(最近最少使用)算法進行淘汰.

  • volatile-lfu 4.0 版本新增,當內存不足以容納新寫入數據時,在過期的 key 中,使用 LFU 算法進行刪除 key.

  • allkeys-lfu 4.0 版本新增,當內存不足以容納新寫入數據時,從所有 key 中使用 LFU 算法進行淘汰.

  • volatile-random 當內存不足以容納新寫入數據時,從設置了過期時間的 key 中,隨機淘汰數據.

  • allkeys-random 當內存不足以容納新寫入數據時,從所有 key 中隨機淘汰數據.

  • volatile-ttl 在設置了過期時間的 key 中,根據過期時間進行淘汰,越早過期的優先被淘汰.(相當于 FIFO, 只不過是局限于過期的 key)

  • noeviction 默認策略,當內存不足以容納新寫入數據時,新寫入操作會報錯.

整體來說 Redis 提供的策略和我們上述介紹的通用策略是基本一致的.只不過 Redis 這里會針對"過期key"和"全部 key"做分別處理

*緩存使用注意事項

緩存預熱(Cache Warm-up)

????????系統重啟或新服務上線時緩存為空,若大量請求涌入,會直接查詢數據庫,導致瞬時壓力過大。

????????緩存預熱是指在系統啟動或高并發場景來臨前,主動將熱點數據加載到緩存中,避免首次請求直接穿透到數據庫。

????????熱點數據可以基于之前介紹的統計的方式生成即可,這份熱點數據不一定非得那么"準確”,只要能幫助MySQL抵擋大部分請求即可.隨著程序運行的推移,緩存的熱點數據會逐漸自動調整,來更適應當前情況.


?緩存穿透(Cache Penetration)

????????緩存穿透是指查詢一個不存在的數據(緩存和數據庫中均無),可能被惡意攻擊利用,導致每次請求都直接訪問數據庫,如果存在很多像這樣的數據,并進行反復查詢,一樣會給MySQL帶來壓力。

原因

  • 務邏輯缺陷:缺少必要的參數校驗工作,導致非法的key也進行了查詢(典型)

  • 開發/運維的誤操作:不小心將某一部分數據刪除了

  • 惡意攻擊:頻繁請求無效參數(如不存在的 ID)。

解決方案

可以通過 加強監控報警,改進業務邏輯

此外可以通過以下方式降低問題的嚴重性

1、緩存空對象(Null Caching):對不存在的數據也緩存一個空值(設置較短過期時間)。

if (data == null) {redis.set(key, "NULL", 300); // 緩存空值,5分鐘過期
}

2、布隆過濾器(Bloom Filter):在緩存層前加布隆過濾器,快速判斷數據是否存在。

優點:內存占用低。

缺點:存在誤判率(需權衡誤判率和內存)。

布隆過濾器本質是結合了hash+bitmap,以較小的空間開銷,以較快的響應速度,實現針對key是否存在進行判斷


緩存雪崩(Cache Avalanche)·

緩存雪崩是指大量緩存key同時失效,導致所有請求直接訪問數據庫,引發數據庫壓力激增甚至崩潰。

原因

  • 緩存過期時間集中:在短時間內,所有緩存設置為相同 TTL(Time-To-Live)。

  • 緩存服務故障:Redis 宕機。

解決方案

1、隨機過期時間:在基礎 TTL 上增加隨機值(如?TTL + random(0, 300s))。

2、高可用架構:采用 Redis 集群(主從、哨兵、Cluster 模式)避免單點故障。

3、持久化緩存:對部分核心數據設置永不過期,通過后臺更新數據。


緩存擊穿(Cache Breakdown)

緩存擊穿是指某個熱點數據過期時,大量并發請求直接穿透到數據庫,導致數據庫壓力驟增。

解決方案

  • 基于統計的方式發現熱點 key,并設置永不過期。
  • 進行必要的服務降級,例如訪問數據庫的時候使用分布式鎖,限制同時請求數據庫的并發數。
  • 熱點數據永不過期:通過后臺任務定期更新數據。


對比總結

問題觸發條件核心原因解決方案
緩存預熱冷啟動或高并發前緩存初始為空提前加載熱點數據
緩存穿透查詢不存在的數據數據不存在于緩存和數據庫布隆過濾器、緩存空對象、參數校驗
緩存雪崩大量緩存同時失效緩存過期時間集中或服務宕機隨機過期時間、高可用架構
緩存擊穿熱點數據過期高并發請求同一熱點數據互斥鎖、邏輯過期時間、永不過期

二、Redis分布式鎖

????????在一個分布式的系統中,也會涉及到多個節點訪問同一個公共資源的情況,此時就需要通過 鎖 來做互斥控制,避免,出現類似于"線程安全"的問題而 java 的 synchronized 或者 C++ 的 std:mutex,這樣的鎖都是只能在當前進程中生效,在分布式的這種多個進程多個主機的場景下就無能為力了此時就需要使用到分布式鎖.

????????Redis 分布式鎖是一種利用 Redis 實現跨進程、跨服務器資源互斥訪問的機制,常用于解決分布式系統中的并發競爭問題(如秒殺扣庫存、任務調度等場景)。

分布式鎖的基本實現

原理很簡單:就是通過一個鍵值對來標識鎖的狀態,是一個簡單的互斥鎖

Redis中提供了setnx正好適用這個場景:即key不存在就設置,存在則直接失敗

為了防止出現 當服務器1 加鎖之后,開始處理業務的過程中,如果 服務器1 意外宕機了,就會導致解鎖操作(刪除該key)不能執行.就可能引起其他服務器始終無法獲取到鎖的情況,需要引入過期時間

set nx ex

注意此處設置過期時間爺需要使用一個命令來設置,由于redis的多個命令之間不存在關聯,不是原子的,可能會存在setnx成功后,expire失敗的情況

為了防止出現,由于業務邏輯產生的誤解鎖問題,意外導致服務器1加鎖之后,被服務器2解鎖的情況,需要引入校驗id

????????給不同的服務器進行編號,作為唯一的身份id,且在設置key-value的時候,key是針對需要加鎖的資源,value是持有鎖的服務器編號

????????解鎖的時候, 先查詢一下這個鎖對應的服務器編號然后判定一下這個編號是否就是當前執行解鎖的服務器編號如果是, 才能真正執行 del. 如果不是,就失敗,通過上述校驗,就可以有效避免,"誤解鎖"。

????????在解鎖的操作的時候由于需要 驗證+解鎖 這兩步操作,這兩步操作也不是原子的,會出現問題此時又需要 引入Lua腳本(Redis本身支持Lua作為內嵌腳本)

if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) 
elsereturn 0 
end;

一個Lua腳本會被redis服務器作為原子來執行

為了防止出現,在任務還未完成執行的時候,我們設置的key就先過期了,這會導致鎖的提前失效

把key的過期時間設置的足夠長,比如 30s,是否能解決這個問題呢?

????????很明顯,設置多長時間合適,是無止境的.即使設置再長,也不能完全保證就沒有提前失效的情況.而且如果設置的太長了,萬一對應的服務器掛了,此時其他服務器也不能及時的獲取到鎖.因此相比于設置一個固定的長時間,不如動態的調整時間更合適.

引入watch dog(看門狗)

????????“Watchdog 看門狗” 是一種監控機制,在這里通過加鎖服務器(業務服務器非redis服務器)上的一個單獨線程,通過這個線程來對鎖的過期時間進行續約。

舉個具體的例子:

初始情況下設置過期時間為 10s.同時設定看門狗線程每隔 3s 檢測一次那么當 3s 時間到的時候,看門狗就會判定當前任務是否完成.

  • 如果任務已經完成,則直接通過 lua 腳本的方式,釋放鎖(刪除 key).
  • 如果任務未完成,則把過期時間重寫設置為 10s.(即“續約")

? ? ? ? 實際業務中的 Redis 一般是以集群的方式部署的. 那么就可能出現以下比較極端的情況:

????????服務器 1 向 master 節點進行加鎖操作。這個寫入 key 的過程剛剛完成,master 掛了;slave 節點升級成了新的 master 節點。但是由于剛才寫入的這個 key 尚未來得及同步給 slave 呢,此時就相當于服務器 1 的加鎖操作形同虛設了,服務器 2 仍然可以進行加鎖 (即給新的 master 寫入 key. 因為新的 master 不包含剛才的 key).

????????為了解決這個問題,Redis 的作者提出了 Redlock 算法.

????????Redlock 算法是 Redis 作者 antirez 提出的分布式鎖算法,用于解決分布式環境下鎖的可靠性和有效性問題 ,它的核心是特別是避免在多個 Redis 實例場景中出現單點故障、死鎖和鎖丟失等情況

?

????????引入一組 Redis 節點,其中每一組 Redis 節點都包含一個主節點和若干從節點,并且組和組之間存儲的數據都是一致的,相互之間是"備份"關系(而并非是數據集合的一部分,這點有別于 Redis cluster)加鎖的時候,按照一定的順序,寫多個 master 節點,在寫鎖的時候需要設定操作的"超時時間".比如50ms.即如果 setnx 操作超過了 50ms 還沒有成功,就視為加鎖失敗.

????????如果給某個節點加鎖失敗,就立即再嘗試下一個節點,當加鎖成功的節點數超過總節點數的一半,才視為加鎖成功。同理,在釋放鎖的時候也需要將所有節點都進行解鎖操作(哪怕是之間超時的節點,也要嘗試解鎖,盡量保證邏輯的嚴密)

????????如上圖,一共五個節點,三個加鎖成功,兩個失敗,此時視為加鎖成功,這樣的話,即使有某些節點掛了,也不影響鎖的正確性。


輕則失本,躁則失君。 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ????????????????????????????????????????? ——老子

🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀

以上,就是本期的全部內容啦,若有錯誤疏忽希望各位大佬及時指出💐

? 制作不易,希望能對各位提供微小的幫助,可否留下你免費的贊呢🌸?

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

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

相關文章

深入解析路由策略:從流量控制到策略實施

一、網絡流量雙平面解析 在路由策略的設計中,必須明確區分兩個關鍵平面: 1. 控制層面(Control Plane) ??定義??:路由協議傳遞路由信息形成的邏輯平面(如OSPF的LSA、RIP的Response報文)?…

從杰夫?托爾納看 BPLG 公司的技術創新與發展

在科技與商業緊密交織的時代,企業的技術領導者在推動組織前行、應對復雜多變的市場環境中扮演著極為關鍵的角色。《對話 CTO,駕馭高科技浪潮》的第 6 章聚焦于杰夫?托爾納及其所在的 BPLG 公司,為我們展現了一幅技術驅動企業發展的生動圖景&…

UniRepLknet助力YOLOv8:高效特征提取與目標檢測性能優化

文章目錄 一、引言二、UniRepLknet 的框架原理(一)架構概述(二)架構優勢 三、UniRepLknet 在 YOLOv8 中的集成(一)集成方法(二)代碼實例 四、實驗與對比(一)對…

比較Facebook與其他社交平臺的隱私保護策略

在這個數字化的時代,隱私保護已成為用戶和社交平臺共同關注的核心議題。Facebook,作為全球最大的社交網絡平臺之一,其隱私保護策略一直受到廣泛的關注和討論。本文將對Facebook的隱私保護策略與其他社交平臺進行比較,以幫助用戶更…

數據結構--樹

一、樹的概念 樹是由n(n≥0)個節點組成的有限集合,它滿足以下條件: 1. 當n0時,稱為空樹 2. 當n>0時,有且僅有一個特定的節點稱為根節點(root) 3. 其余節點可分為m(m≥0)個互不相交的有限集合,每個集合本身又是一…

Linux `ifconfig` 指令深度解析與替代方案指南

Linux `ifconfig` 指令深度解析與替代方案指南 一、核心功能與現狀1. 基礎作用2. 版本適配二、基礎語法與常用操作1. 標準語法2. 常用操作速查顯示所有接口信息啟用/禁用接口配置IPv4地址修改MAC地址(臨時)三、高級配置技巧1. 虛擬接口創建2. MTU調整3. 多播配置4. ARP控制四…

什么是分布式光伏系統?屋頂分布式光伏如何并網?

政策窗口倒計時!分布式光伏如何破局而立? 2025年,中國分布式光伏行業迎來關鍵轉折: ? "430"落幕——搶裝潮收官,但考驗才剛開始; ? "531"生死線——新增項目全面市場化交易啟動&…

Cluster Interconnect in Oracle RAC

Cluster Interconnect in Oracle RAC (文檔 ID 787420.1)?編輯轉到底部 In this Document Purpose Scope Details Physical Layout of the Private Interconnect Why Do We Need a Private Interconnect ? Interconnect Failure Interconnect High Availability Private Inte…

.Net HttpClient 使用準則

HttpClient 使用準則 System.Net.Http.HttpClient 類用于發送 HTTP 請求以及從 URI 所標識的資源接收 HTTP 響應。 HttpClient 實例是應用于該實例執行的所有請求的設置集合,每個實例使用自身的連接池,該池將其請求與其他請求隔離開來。 從 .NET Core …

【PostgreSQL】數據庫主從庫備份與高可用部署

文章目錄 一、架構設計原理二、部署清單示例2.1 StatefulSet配置片段2.2 Service配置三、配置詳解3.1 主節點postgresql.conf3.2 從節點配置四、初始化流程4.1 創建復制用戶4.2 配置pg_hba.conf五、故障轉移示例5.1 自動切換腳本5.2 手動提升從節點六、監控與維護6.1 關鍵監控指…

JavaScript 數組去重:11 種方法對比與實戰指南

文章目錄 前言一、使用 Set 數據結構二、使用 filter indexOf三、使用 reduce 累加器四、雙重 for 循環五、利用對象屬性唯一性六、先排序后去重七、使用 Map 數據結構八、使用 includes 方法九、優化處理 NaN 的 filter 方法十、利用 findIndex十一.利用Set和展開運算符處理多…

ai agent(智能體)開發 python3基礎14:在python 中 總能看到方法里面套方法,那什么時候用這種方式合適呢?

讓人頭疼的方法嵌套還是要去了解的 在 Python 中,方法內部嵌套方法(即在類的方法中定義另一個函數)是一種常見的代碼組織技巧,它可以在特定場景下帶來以下好處: 1. 代碼復用與邏輯封裝 如果某個方法內部有重復的邏輯…

Yocto項目實戰經驗總結:從入門到高級的全面概覽

本文面向開發者和實際項目經驗者,分享經過大量實戰積累的 Yocto 項目工程經驗和基礎技巧。本文簡明但精彩,應用和觀察相結合,充分適合做為全面進階 Yocto 項目開發的實用指南。 一、入門理解:Yocto 是什么?規劃如何開始…

添加物體.

在cesium中我們可以添加物體進入地圖.我們以廣州塔為例 //生成廣州塔的位置var position2 Cesium.Cartesian3.fromDegrees(113.3191,23.109,100)viewer.camera.setView({//指定相機位置destination: position2, 運行后如圖 我們使用cesium官網提供的代碼為廣州塔在地圖上標點…

正則表達式非捕獲分組?:

一個使用 Java 正則表達式的具體例子,展示了 (ab) 和 (?:ab) 的不同: 示例 1:使用 (ab)(捕獲分組) import java.util.regex.*; public class RegexExample { public static void main(String[] args) { …

ragflow報錯:KeyError: ‘\n “序號“‘

環境: ragflowv 0.17.2 問題描述: ragflow報錯:KeyError: ‘\n “序號”’ **1. 推薦表(輸出json格式)** [{"},{},{"},{} ]raceback (most recent call last): May 08 20:06:09 VM-0-2-ubuntu ragflow-s…

Spring Boot-8啟動涉及的監聽器(擴展點)

從出現時間上看: org.springframework.context.ApplicationListener,Spring 1.0開始出現 org.springframework.context.ApplicationContextInitializer,Spring 3.1開始出現 org.springframework.boot.SpringApplicationRunListener&#x…

如何啟動vue項目及vue語法組件化不同標簽應對的作用說明

如何啟動vue項目及vue語法組件化不同標簽應對的作用說明 提示:幫幫志會陸續更新非常多的IT技術知識,希望分享的內容對您有用。本章分享的是node.js和vue的使用。前后每一小節的內容是存在的有:學習and理解的關聯性。【幫幫志系列文章】&…

思考:(linux) tmux 超級終端快速入門的宏觀思維

tmux 工具集合 GitHub - rothgar/awesome-tmux: A list of awesome resources for tmux 要點: 習慣性思維的變換與宿主機之間的雙向復制、粘貼手動備份全部窗口,以及還原自定義窗格提示信息TPM 插件的安裝思想別名 在有些場景里,可能無法…

Python實例題:Python協程詳解公開課

目錄 Python實例題 題目 課程目標 課程內容規劃 1. 課程開場(5 分鐘) 2. 基礎概念講解(15 分鐘) 并發與并行: 線程與進程: 3. Python 協程的實現方式(20 分鐘) 生成器實現…