緩存-變更事件捕捉、更新策略、本地緩存和熱key問題

緩存-基礎知識

熟悉計算機基礎的同學們都知道,服務的存儲大多是多層級的,呈現金字塔類型。通常來說本機存儲比通過網絡通信的外部存儲更快(現在也不一定了,因為網絡傳輸速度很快,至少可以比一些過時的本地存儲設備速度快)。常見的本機存儲和網絡設備有:

  1. 本機:從快到慢有CPU寄存器、CPU多級緩存、內存、磁盤
  2. 網絡設備:從快到慢有Redis這類的基于內存的緩存,MySQL這類持久化數據庫,RPC調用(如果假設RPC調用的下游也需要調用緩存和數據庫等組件)等

那么使用緩存有什么好處呢?通俗地講就是“快”,準確地說是讀取快,每秒承載的訪問次數多,這樣服務端每秒能處理的請求就更多,服務總體的吞吐量就大。具體來說,基于內存的存儲比基于磁盤的快,所以大家常常使用Redis作為MySQL的緩存;而本機的內存緩存又會比通過網絡調用Redis快,因此現在一些互聯網公司也在搭建本地緩存(Local Cache)來緩解Redis的壓力。

另外需要看到緩存的缺點:緩存增加了數據的副本份數,所以寫入的份數也增加了,寫入性能必然降低,一致性管理成本必然增加。所以緩存基本都是用于讀多寫少,且不要求強一致性的場景。

更新事件捕獲與更新緩存的策略

在常見的在線服務調用中,我們會構建緩存來避免對數據庫和下游的調用,來減少數據庫壓力和對下游服務的壓力。當相關數據改變時,可以使緩存失效或者更新緩存。那么這里引入了兩個問題,第一個是如何知道緩存該被更新了,第二個是怎么選擇刪除緩存和更新緩存的策略。

更新事件捕獲

通常有兩種方式來捕獲更新事件:

  1. 服務代碼主動更新:用戶操作涉及修改數據時,處理用戶請求的服務代碼會刷新緩存和數據庫里的數據
  2. 訂閱數據庫binlog延遲更新:數據庫配置binlog,并通過一些binlog同步工具將數據庫變更記錄發送到消息隊列。服務端訂閱消息隊列的指定話題獲取變更記錄,進而更新緩存數據

1#通常被用于更新自己團隊的數據,而2#通常用于更新下游服務,或者作為緩存更新失敗時的兜底邏輯。畢竟下游是數據的生產方,讓下游更新緩存的時候通知所有上游不太現實,這種基于訂閱模式的事件捕獲機制有很強的擴展性。

更新策略選擇

具體策略(刪除或更新)的選擇通常取決于數據是否在本次處理中已經計算好了。比方說收到一條變更消息,說用戶的年齡已經被更新了。與年齡相關的可能還有用戶對好友的可見性、用戶是否可以接收其它用戶的信息等內容,它們都可能隨著年齡的更改而同步變化,而這些信息目前還是未知的——除非再次請求下游。因此對于這種情況,我們會將相關的數據直接刪除。由于年齡的更新頻率非常低,所以我們可以認為刪除相關緩存的操作對其性能的總體影響很小。

但是如果需要更新的字段比較獨立,不會影響別的字段,比方說用戶昵稱或者頭像,那直接更新對應的數據即可

本地緩存(Local Cache)

說完了更新緩存的策略,我們再來看看最近業界比較流行的Local Cache的實現。這里的local,指的是服務器的單實例,也就是每個實例都會有自己的緩存副本。它的適用條件是讀請求次數遠遠大于寫,且數據總量較小。比如對于Redis來說,性能指標的表現就是內存利用率低,但是CPU占用率很高。這對于現在的微服務架構來說比較常見,每個服務需要管理的字段不多,但是請求量巨大,比如對于社交應用來說,用戶年齡相關的字段就符合這種訪問模式,每次請求基本都要讀取年齡字段,但是數據量卻很小。

讀寫過程

本地緩存的讀取策略很簡單,不過就是在Redis之前加了個讀取本地緩存的過程,如果緩存不存在就從更高層級緩存拉取。

那如何更新緩存呢?由于分布式系統的特性,每個服務器實例都有自己的內存,除非使用特別的路由策略,讓同一個用戶的請求只打到同一臺機器上,否則更新緩存是很頭疼的事情:用戶的緩存可能存在于不同機器上,如何讓多臺服務器的緩存同時失效?因此在分布式的世界中,使用Redis這樣的集中緩存比較常見,只要存儲是集中式的且從計算單元中分離出來,服務就可以做成無狀態的,減少數據狀態的管理成本。

回憶一下我們剛剛說過的捕獲更新和緩存更新方式,其實本地緩存的更新也不出其右:用binlog+消息隊列同步數據庫變更,所有啟用本地緩存的服務器實例,都需要監聽消息隊列里的變更消息,并刷新(或者刪除)本地緩存。唯一的不同是,如果需要刷新Redis緩存,那我們可以把刷新緩存的服務和處理用戶請求的服務獨立開來;但是本地緩存的緩存更新邏輯得和服務器邏輯都放在同一個實例中。這也很好理解,Redis是集中式的緩存,而本地緩存和服務器實例牢牢捆綁在一起。

本地緩存的優勢

之前說過,本地緩存適用于讀遠遠大于寫,且請求量大但是緩存數據總量較小的場景。為什么呢?因為假如CPU和內存利用率都很高,即使本地緩存降低了Redis的讀取次數,那也只能降低Redis的CPU占用率,而內存利用率還是很高。內存利用率和緩存數據總量有關,所以多一層緩存不會減少緩存的數據總量。根據木桶原理,即使CPU利用率降低了,由于內存的需求還在,也不能縮減實例的數量,所以并不能節約太多錢(可以降低一下CPU規格,但是一臺生產環境實例的基本價格在那,降低或提高規格帶來的費用變化并不大)。

那有人問了,本地緩存不是也提高了服務端的內存占用,這方面的費用不算了嗎?剛剛說過,生產環境里一臺實例的基礎費用不低,而且增加一些內存帶來的費用相比于減少Redis實例數量來說很小。而且本來本地緩存就適合數據總量小的場景,因此增加一些內存容量(比如4-8G)就能有客觀的命中率(比如50%+)。

我相信還有人會問,增加了更新鏈路不是也帶來了額外費用嗎?這就要再強調一下本地緩存的適用場景:讀遠遠大于寫,比如大了兩個數量級。這樣緩存的更新頻率足夠低,緩存更新鏈路所需的資源也足夠少。

緩存常見問題

緩存如何解決熱點問題

熱點問題是緩存遇到的常見問題之一,比如強如Redis也有其吞吐量上限,而Redis同一個key只能存儲在一個分片、一個進程里,可能還是無法應付一些熱點用戶的數據訪問(比如某個熱點問題,某個熱點主播)。《數據密集型系統》給出兩個比較籠統的方案:

  1. 給熱key實例更多資源:比如將其單獨存儲在一個實例中,或者給它分配更好的機器。但是這種方案很容易就會達到上限,因為不具備水平擴展能力。
  2. 拆key:比如某個用戶(id為1234)是熱點,那就將它的數據存在100個副本中,每個副本的key是id后面加上兩位數。這樣當看到1234這個id時,服務端會自動在后面加上兩位隨機數,生成形如123401,123402…123499這樣的id,再到Redis里訪問。這相當于把一個熱key的訪問壓力分散為原來的1/100。這種方式明顯是優化了讀請求。如果是寫請求比較重,那也可以做類似的事情,將寫分散到不同的實例上,但讀的時候就需要讀取所有的100個分片。

由于1#不具備水平擴展能力,所以一些業界流行的做法都采用了類似2#的方式,只不過實現起來各有特點,比方說在Redis之前加一層緩存代理,這層代理也是一個分布式服務,一旦有熱key存在,就會將熱key緩存到代理服務的本地緩存里,實現了類似于2#中拆key的效果(因為代理服務也是分布式的,多個實例都可以緩存熱key的內容,進而分散了Redis單實例的壓力)

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

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

相關文章

報表工具DevExpress .NET Reports v25.1新版本亮點:AI驅動的擴展

DevExpress Reporting是.NET Framework下功能完善的報表平臺,它附帶了易于使用的Visual Studio報表設計器和豐富的報表控件集,包括數據透視表、圖表,因此您可以構建無與倫比、信息清晰的報表。 DevExpress Reporting控件日前正式發布了v25.1…

kubernetes中pod的管理及優化

目錄 2 資源管理方式 2.1 命令式對象管理 2.2 資源類型 2.2.1 常用的資源類型 2.2.2 kubectl常見命令操作 2.3 基本命令示例 2.4 運行和調試命令示例 2.5 高級命令示例 3 pod簡介 3.1 創建自主式pod(生產環境不推薦) 3.1.1 優缺點 3.1.2 創建…

解釋一下,Linux,shell,Vmware,Ubuntu,以及Linux命令和shell命令的區別

Linux 操作系統概述Linux 是一種開源的類 Unix 操作系統內核,由 Linus Torvalds 于 1991 年首次發布。作為現代計算的基礎設施之一,它具有以下核心特征:多用戶多任務特性允許多個用戶同時操作系統資源,而模塊化設計使其能夠適應從…

Windows 系統中,添加打印機主要有以下幾種方式

在 Windows 系統中,添加打印機主要有以下幾種方式,我將從最簡單到最復雜為您詳細介紹。 方法一:自動安裝(推薦首選) 這是 Windows 10 和 Windows 11 中最簡單、最現代的方法。系統會自動搜索網絡(包括無線和有線網絡)上可用的打印機并安裝驅動程序。 操作步驟: 進入…

Mixture of Experts Guided by Gaussian Splatters Matters

Mixture of Experts Guided by Gaussian Splatters Matters: A new Approach to Weakly-Supervised Video Anomaly Detection ICCV2025 https://arxiv.org/pdf/2508.06318 https://github.com/snehashismajhi/GS-MoEAbstract 視頻異常檢測(VAD)是一項具有…

SeaTunnel Databend Sink Connector CDC 功能實現詳解

Databend 是一個面向分析型工作負載優化的 OLAP 數據庫,采用列式存儲架構。在處理 CDC(Change Data Capture,變更數據捕獲)場景時,如果直接執行單條的 UPDATE 和 DELETE 操作,會嚴重影響性能,無…

算法230. 二叉搜索樹中第 K 小的元素

題目:給定一個二叉搜索樹的根節點 root ,和一個整數 k ,請你設計一個算法查找其中第 k 小的元素(從 1 開始計數)。示例 1:輸入:root [3,1,4,null,2], k 1 輸出:1 示例 2&#xff1…

Seaborn數據可視化實戰:Seaborn多變量圖表繪制高級教程

Seaborn多變量圖表實戰:從數據到洞察 學習目標 本課程將帶領學員深入了解Seaborn庫中用于繪制多變量圖表的高級功能,包括聯合圖(Joint Plot)、對角線圖(Pair Plot)等。通過本課程的學習,學員將能…

【數智化人物展】首衡科技CTO李蒙:算法會過時,數據會貶值,只有系統智能才具未來性

李蒙本文由首衡科技CTO李蒙投遞并參與由數智猿數據猿上海大數據聯盟共同推出的《2025中國數智化轉型升級先鋒人物》榜單/獎項評選。大數據產業創新服務媒體——聚焦數據 改變商業“算法會過時,數據會貶值。”當我第一次在內部戰略會上拋出這句話時,現場…

word——將其中一頁變成橫向

在word中如何將其中一頁變成橫向? 在需要橫向的這一頁和上一頁插入分節符(連續) 1.點擊布局→分隔符→分節符(連續) 2.在所需要橫向頁將紙張方向改為橫向即可。

使用WORD實現論文格式的樣式化制作【標題樣式、自動序列、頁號(分節)、自動目錄(修改字體類型)】

背景 每家院校對論文的格式都有一系列的特定要求,相應的會有一份格式標準的說明文檔,該說明文檔中會羅列對文檔各個項的格式標準要求(例如:題目、1級標題、2級標題、頁號、每個級別的字體字號,行距,段前段…

分享一個免費開源的網站跟蹤分析工具Open-Web-Analytics(和GoogleAnalytics一樣)

做獨立網站的福音,這個是免費開源的,可增改性強。 開源地址:https://github.com/Open-Web-Analytics/Open-Web-Analytics 下載源碼包 接著下載PHP工具:我用XP小皮 phpstudy_pro 地址:phpStudy - Windows 一鍵部署 …

Maxscript如何清理3dMax場景?

在3ds Max的創作過程中,隨著項目的推進,場景往往會積累許多冗余元素,如孤立幫助對象、隱藏對象以及空層等,它們不僅讓場景顯得雜亂無章,還會占用資源、降低視口性能,影響工作效率。別擔心,在本教程中,我們將為大家帶來實用妙招——通過簡單的Maxscript腳本片段,快速清…

JavaScript 性能優化實戰:從分析到落地的全指南

一、引言:為什么 JS 性能優化至關重要?用戶體驗的直接影響:加載慢、交互卡頓如何流失用戶(引用 Google 研究:頁面加載延遲 1 秒,轉化率下降 7%)業務價值關聯:性能優化對 SEO、留存率…

線性回歸學習筆記

一、線性回歸簡介1. 核心定義線性回歸是一種通過屬性的線性組合進行預測的線性模型,核心目標是找到一條直線(二維)、一個平面(三維)或更高維的超平面,使模型的預測值與真實值之間的誤差最小化。2. 適用場景…

Kotlin 中適用集合數據的高階函數(forEach、map、filter、groupBy、fold、sortedBy)

在 Kotlin 中,高級函數(Higher-Order Functions)是一個非常強大的特性。高級函數是指可以將函數作為參數傳遞,或者將函數作為返回值返回的函數。這種特性使得代碼更加靈活和可復用。 使用高級函數可以方便地對集合進行操作,如 map、filter、reduce 等。 在事件驅動的編程中…

Redis 哈希表的核心——`dictEntry` 結構體

接上一篇 Redis 哈希表的本質:數組里存的是什么 Redis 哈希表的核心——dictEntry 結構體,是真正承載我們存儲的鍵值對數據的那個結構。 它的定義非常簡潔,但設計得很巧妙。以下是其 C 語言代碼(在 Redis 源碼 src/dict.h 中&a…

Jsqlparser + Freemarker + Vue3 數據透視報表設計方案

1. 目標與前置條件目標:基于 JSQLParser FreeMarker Vue3 構建一套“可配置的數據透視報表”能力,實現從任意基礎 SQL/視圖出發,按維度/指標靈活聚合、篩選、排序、分頁、導出,并支持鉆取、聯動、TopN、同比環比等常見分析操作。…

SpringBoot3 Ruoyi芋道管理后臺vben5.0

新技術棧(Vue3、Vite6、TypeScript、SpringBoot3/SpringCloud基于Vben5.0最新版本,全面采用Vue3 Vite6 Ant Design Vue TypeScript技術棧,并同時支持SpringBoot3單體架構與SpringCloud微服務架構前端技術棧:Vue3 Vite6 TS A…

K8S - NetworkPolicy的使用

1 前置條件2 控制范圍3 隔離類型4 如何識別5 主要字段6 案例演示 前置條件 網絡策略通過網絡插件來實現。 要使用網絡策略,你必須使用支持 NetworkPolicy 的網絡解決方案。 創建一個 NetworkPolicy 資源對象而沒有控制器來使它生效的話,是沒有任何作用的…