6、架構-服務端緩存

????????為系統引入緩存之前,第一件事情是確認系統是否真的需要緩 存。從開發角度來說,引入緩存會提 高系統復雜度,因為你要考慮緩存的失效、更新、一致性等問題;從運維角度來說,緩存會掩蓋一些缺 陷,讓問題在更久的時間以后,出現在距離發生現場更遠的位置上; 從安全角度來說,緩存可能會泄漏某些保密數據,也是容易受到攻擊 的薄弱點。冒著上述種種風險,仍能說服你引入緩存的理由,總結起 來無外乎以下兩種。

  • 為緩解CPU壓力而引入緩存:譬如把方法運行結果存儲起來、 把原本要實時計算的內容提前算好、對一些公用的數據進行復用,這 可以節省CPU算力,順帶提升響應性能。
  • 為緩解I/O壓力而引入緩存:譬如把原本對網絡、磁盤等較慢 介質的讀寫訪問變為對內存等較快介質的訪問,將原本對單點部件 (如數據庫)的讀寫訪問變為對可擴縮部件(如緩存中間件)的訪 問,順帶提升響應性能。

????????請注意,緩存雖然是典型以空間換時間來提升性能的手段,但它 的出發點是緩解CPU和I/O資源在峰值流量下的壓力,“順帶”而非 “專門”地提升響應性能。這里的言外之意是如果可以通過增強CPU、 I/O本身的性能(譬如擴展服務器的數量)來滿足需要的話,那升級硬 件往往是更好的解決方案,即使需要一些額外的投入成本,也通常要 優于引入緩存后可能帶來的風險。

緩存屬性

????????有不少軟件系統最初的緩存功能是以HashMap或者 ConcurrentHashMap為起點演進的。當開發人員發現系統中某些資源的 構建成本比較高,而這些資源又有被重復使用的可能時,會很自然地 產生“循環再利用”的想法,將它們放到Map容器中,待下次需要時取 出重用,避免重新構建,這種原始樸素的復用就是最基本的緩存。不 過,一旦我們專門把“緩存”看作一項技術基礎設施,一旦它有了通 用、高效、可統計、可管理等方面的需求,其中要考慮的因素就變得 復雜起來。通常,我們設計或者選擇緩存至少會考慮以下四個維度的 屬性。

  • 吞吐量:緩存的吞吐量使用OPS值(每秒操作數,Operation per Second,ops/s)來衡量,反映了對緩存進行并發讀、寫操作的效率, 即緩存本身的工作效率高低。
  • 命中率:緩存的命中率即成功從緩存中返回結果次數與總請求 次數的比值,反映了引入緩存的價值高低,命中率越低,引入緩存的 收益越小,價值越低。
  • 擴展功能:即緩存除了基本讀寫功能外,還提供哪些額外的管 理功能,譬如最大容量、失效時間、失效事件、命中率統計,等等。
  • 分布式緩存:緩存可分為“進程內緩存”和“分布式緩存”兩 大類,前者只為節點本身提供服務,無網絡訪問操作,速度快但緩存 的數據不能在各服務節點中共享,后者則相反。

吞吐量

????????緩存的吞吐量只在并發場景中才有統計的意義,因為若不考慮并 發,即使是最原始的、以HashMap實現的緩存,訪問效率也已經是常量 時間復雜度(即O(1)),其中涉及碰撞、擴容等場景的處理屬于數據 結構基礎,這里不再展開。但HashMap并不是線程安全的容器,如果要 讓它在多線程并發下正確地工作,就要用 Collections.synchronizedMap進行包裝,這相當于給Map接口的所有 訪問方法都自動加全局鎖;或者改用ConcurrentHashMap來實現,這相 當于給Map的訪問分段加鎖(從JDK 8起已取消分段加鎖,改為 CAS+Synchronized鎖單個元素)。無論采用怎樣的實現方法,這些線 程安全措施都會帶來一定的吞吐量損失。

命中率與淘汰策略

????????有限的物理存儲決定了任何緩存的容量都不可能是無限的,所以 緩存需要在消耗空間與節約時間之間取得平衡,這要求緩存必須能夠 自動或者人工淘汰掉緩存中的低價值數據。考慮到由人工管理的緩存 淘汰主要取決于開發者如何編碼,不能一概而論,這里只討論由緩存 自動進行淘汰的情況。筆者所說的“緩存如何自動地實現淘汰低價值 目標”,現在被稱為緩存的淘汰策略。

????????在了解緩存如何實現自動淘汰低價值數據之前,首先要定義怎樣 的數據才算是“低價值”。由于緩存的通用性,這個問題的答案必須 是與具體業務邏輯無關的,只能從緩存工作過程收集到的統計結果來 確定數據是否有價值,通用的統計結果包括但不限于數據何時進入緩 存、被使用過多少次、最近什么時候被使用,等等。一旦確定選擇何 種統計數據,就決定了如何通用地、自動地判定緩存中每個數據的價 值高低,也相當于決定了緩存的淘汰策略是如何實現的。目前,最基 礎的淘汰策略實現方案有以下三種。

  • FIFO(First In First Out):優先淘汰最早進入被緩存的數據。 FIFO的實現十分簡單,但一般來說它并不是優秀的淘汰策略,越是頻 繁被用到的數據,往往會越早存入緩存之中。如果采用這種淘汰策 略,很可能會大幅降低緩存的命中率。
  • LRU(Least Recent Used):優先淘汰最久未被訪問過的數據。 LRU通常會采用HashMap加LinkedList的雙重結構(如LinkedHashMap) 來實現,以HashMap來提供訪問接口,保證常量時間復雜度的讀取性 能,以LinkedList的鏈表元素順序來表示數據的時間順序,每次緩存命 中時把返回對象調整到LinkedList開頭,每次緩存淘汰時從鏈表末端開 始清理數據。對大多數的緩存場景來說,LRU明顯要比FIFO策略合 理,尤其適合用來處理短時間內頻繁訪問的熱點對象。但是如果一些 熱點數據在系統中被頻繁訪問,只是最近一段時間因為某種原因未被 訪問過,那么這些熱點數據此時就會有被LRU淘汰的風險,換句話 說,LRU依然可能錯誤淘汰價值更高的數據。
  • LFU(Least Frequently Used):優先淘汰最不經常使用的數據。 LFU會給每個數據添加一個訪問計數器,每訪問一次就加1,需要淘汰 時就清理計數器數值最小的那批數據。LFU可以解決上面LRU中熱點數 據間隔一段時間不訪問就被淘汰的問題,但同時它又引入了兩個新的 問題。第一個問題是需要對每個緩存的數據專門維護一個計數器,每 次訪問都要更新,但這樣做會帶來高昂的維護開銷;另一個問題是不 便于處理隨時間變化的熱度變化,譬如某個曾經頻繁訪問的數據現在 不需要了,但很難自動將它清理出緩存。

????????緩存的淘汰策略直接影響緩存的命中率,沒有一種策略是完美 的、能夠滿足系統全部需求的。不過,隨著淘汰算法的不斷發展,近 年來的確出現了許多相對性能更好、也更復雜的新算法。以LFU為例, 針對它存在的兩個問題,近年來提出的TinyLFU和W-TinyLFU算法就會 有更好的效果。

分布式緩存

????????相比緩存數據在進程內存中讀寫的速度,一旦涉及網絡訪問,由 網絡傳輸、數據復制、序列化和反序列化等操作所導致的延遲要比內 存訪問高得多,所以對分布式緩存來說,處理與網絡相關的操作是對 吞吐量影響更大的因素,往往也是比淘汰策略、擴展功能更重要的關 注點,這也決定了盡管有Ehcache、Infinispan這類能同時支持分布式 部署和進程內部署的緩存方案,但通常進程內緩存和分布式緩存選型 時會有完全不同的候選對象及考察點。在我們決定使用哪種分布式緩 存前,首先必須確定自己的需求是什么。

????????盡管多級緩存結合了進程內緩存和分布式緩存的優點,但它的代 碼侵入性較大,需要由開發者承擔多次查詢、多次回填的工作,也不 便于管理,如超時、刷新等策略都要設置多遍,數據更新更是麻煩, 很容易出現各個節點的一級緩存以及二級緩存中數據不一致的問題。 所以,必須“透明”地解決以上問題,才能使多級緩存具有實用的價 值。一種常見的設計原則是變更以分布式緩存中的數據為準,訪問以 進程內緩存的數據優先。大致做法是當數據發生變動時,在集群內發 送推送通知(簡單點的話可采用Redis的PUB/SUB,求嚴謹的話可引入 ZooKeeper或etcd來處理),讓各個節點的一級緩存中的相應數據自動 失效。當訪問緩存時,提供統一封裝好的一、二級緩存聯合查詢接 口,接口外部是只查詢一次,接口內部自動實現優先查詢一級緩存, 未獲取到數據再自動查詢二級緩存的邏輯。?

緩存風險

1.緩存穿透

????????緩存的目的是緩解CPU或者I/O的壓力,譬如對數據庫做緩存,大 部分流量都從緩存中直接返回,只有緩存未能命中的數據請求才會流 到數據庫中,這樣數據庫壓力自然就減小了。但是如果查詢的數據在 數據庫中根本不存在,緩存里自然也不會有,這類請求的流量每次都 不會命中,且每次都會觸及末端的數據庫,緩存就起不到緩解壓力的 作用了,這種查詢不存在的數據的現象被稱為緩存穿透

????????緩存穿透有可能是業務邏輯本身就存在的固有問題,也有可能是 惡意攻擊所導致。為了解決緩存穿透問題,通常會采取下面兩種辦 法。

  • 對于業務邏輯本身不能避免的緩存穿透,可以約定在一定時間 內對返回為空的Key值進行緩存(注意是正常返回但是結果為空,不 應把拋異常的也當作空值來緩存),使得在一段時間內緩存最多被穿 透一次。如果后續業務在數據庫中對該Key值插入了新記錄,那應當 在插入之后主動清理掉緩存的Key值。如果業務時效性允許的話,也 可以對緩存設置一個較短的超時時間來自動處理。
  • 對于惡意攻擊導致的緩存穿透,通常會在緩存之前設置一個布 隆過濾器來解決。所謂惡意攻擊是指請求者刻意構造數據庫中肯定不 存在的Key值,然后發送大量請求進行查詢。布隆過濾器是用最小的 代價來判斷某個元素是否存在于某個集合的辦法。如果布隆過濾器給 出的判定結果是請求的數據不存在,直接返回即可,連緩存都不必去 查。雖然維護布隆過濾器本身需要一定的成本,但比起攻擊造成的資 源損耗仍然是值得的。

2.緩存擊穿

????????我們都知道緩存的基本工作原理是首次從真實數據源加載數據, 完成加載后回填入緩存,以后其他相同的請求就從緩存中獲取數據, 以緩解數據源的壓力。如果緩存中某些熱點數據忽然因某種原因失效 了,譬如由于超期而失效,此時又有多個針對該數據的請求同時發送 過來,這些請求將全部未能命中緩存,到達真實數據源中,導致其壓 力劇增,這種現象被稱為緩存擊穿。要避免緩存擊穿問題,通常會采 取下面兩種辦法。

  • 加鎖同步,以請求該數據的Key值為鎖,使得只有第一個請求 可以流入真實的數據源中,對其他線程則采取阻塞或重試策略。如果 是進程內緩存出現問題,施加普通互斥鎖即可,如果是分布式緩存中 出現問題,就施加分布式鎖,這樣數據源就不會同時收到大量針對同 一個數據的請求了。
  • 熱點數據由代碼來手動管理。緩存擊穿是僅針對熱點數據自動 失效才引發的問題,對于這類數據,可以直接由開發者通過代碼來有 計劃地完成更新、失效,避免由緩存的策略自動管理。

3.緩存雪崩

????????緩存擊穿是針對單個熱點數據失效,由大量請求擊穿緩存而給真 實數據源帶來壓力。還有一種可能更普遍的情況,即不是針對單個熱 點數據的大量請求,而是由于大批不同的數據在短時間內一起失效, 導致這些數據的請求都擊穿緩存到達數據源,同樣令數據源在短時間 內壓力劇增。 出現這種情況,往往是因為系統有專門的緩存預熱功能,或者大 量公共數據是由某一次冷操作加載的,使得由此載入緩存的大批數據 具有相同的過期時間,在同一時刻一起失效;也可能是因為緩存服務 由于某些原因崩潰后重啟,造成大量數據同時失效。這種現象被稱為 緩存雪崩。要避免緩存雪崩問題,通常會采取下面三種辦法。

  1. 提升緩存系統可用性,建設分布式緩存的集群
  2. 啟用透明多級緩存,這樣各個服務節點一級緩存中的數據通常 會具有不一樣的加載時間,也就分散了它們的過期時間。
  3. 將緩存的生存期從固定時間改為一個時間段內的隨機時間,譬 如原本是1h過期,在緩存不同數據時,可以設置生存期為55min到 65min之間的某個隨機時間。

4.緩存污染

緩存污染是指緩存中的數據與真實數據源中的數據不一致的現象。這種情況通常是由于開發者在更新緩存時操作不規范造成的,可能導致數據的不一致性。雖然緩存通常不追求強一致性,但最終一致性仍然是必須的。

造成緩存污染的常見原因

  1. 更新緩存不規范:開發者從緩存中獲得某個對象并更新其屬性,但由于某些原因(如業務異常回滾),最終未能成功寫入數據庫,導致緩存中的數據是新的,但數據庫中的數據是舊的。
  2. 操作順序錯誤:如果更新操作的順序不正確,例如先失效緩存后寫數據源,可能會導致緩存和數據源的不一致。

提高緩存一致性的設計模式

為了盡可能地提高使用緩存時的一致性,以下幾種常見的設計模式可以幫助管理緩存更新:

  1. Cache Aside

    • 讀數據:先讀緩存,如果緩存中沒有,再讀數據源,然后將數據放入緩存,再響應請求。
    • 寫數據:先寫數據源,然后失效緩存。

    這種模式的關鍵在于:

    • 先后順序:必須先寫數據源后失效緩存。如果先失效緩存后寫數據源,可能在緩存失效和數據源更新之間存在時間窗口,此時新的查詢請求會讀到舊的數據并重新填充到緩存中,導致數據不一致。
    • 失效緩存:而不是嘗試更新緩存,因為更新過程中數據源可能會被其他請求再次修改,處理多次賦值的時序問題非常復雜。失效緩存可以避免這種情況,確保下次讀取時自動回填最新的數據。
  2. Read/Write Through

    • 讀數據:先讀緩存,如果沒有,再讀數據源,并將數據寫入緩存。
    • 寫數據:先寫緩存,再寫數據源。

    這種模式保證了緩存和數據源的一致性,但實現起來比較復雜,可能會帶來性能開銷。

  3. Write Behind Caching

    • 讀數據:先讀緩存,如果沒有,再讀數據源,并將數據寫入緩存。
    • 寫數據:先寫緩存,然后異步地將數據寫入數據源。

    這種模式通過異步更新數據源,提高了寫操作的性能,但在數據源更新的延遲期間,可能會導致數據不一致。

Cache Aside 模式的局限性

Cache Aside 模式雖然簡潔且成本低,但依然不能完全保證一致性。例如,當某個從未被緩存的數據請求直接流到數據源,如果數據源的寫操作發生在查詢請求之后但回填到緩存之前,緩存中回填的內容可能與數據庫的實際數據不一致。然而,這種情況的發生概率較低,Cache Aside 模式仍然是一個低成本且相對可靠的解決方案。

通過以上設計模式,可以在一定程度上減少緩存污染,確保緩存與數據源之間的一致性,但完全避免是不現實的,特別是在高并發和復雜業務場景下。

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

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

相關文章

npm徹底清理緩存

在使用npm過程中,肯定會遇到清緩存的情況,網上的命令一般為 npm cache clear --force有時筆者在清理緩存之后npm install依然失敗,仔細發現,執行該命令之后npm報了一個警告 npm WARN using --force Recommended protections dis…

代碼隨想錄算法訓練營第27天|● 39. 組合總和● 40.組合總和II● 131.分割回文串

組合總和 題目鏈接 39. 組合總和 - 力扣&#xff08;LeetCode&#xff09; 代碼&#xff1a; class Solution {public List<List<Integer>> res new ArrayList<>();public List<Integer> list new ArrayList<>();public int sum 0;/**…

在nginx中配置反向代理

在nginx中配置反向代理&#xff0c;需要使用proxy_pass指令。以下是一個簡單的nginx反向代理配置示例&#xff1a; server {listen 80;server_name example.com;location / {proxy_pass http://backend_server;proxy_set_header Host $host;proxy_set_header X-Real-IP $remote…

LoadRunner 錄制腳本時提示無Internet訪問/加載慢,如何解決?

LoadRunner 錄制腳本時提示無Internet訪問/加載慢&#xff0c;如何解決&#xff1f; 在使用LoadRunner 12.02 進行錄制腳本時提示無Internet訪問&#xff0c;這是如下圖&#xff1a; 翻譯中文如下&#xff1a; 這里&#xff0c;我認為大家應該都已經點過yes了&#xff0c;但是…

python結構化模式匹配switch-case,Python 3.10中引入,Python的模式匹配(pattern matching)語法

增加了采用模式加上相應動作的 match 語句 和 case 語句 的形式的結構化模式匹配。 模式由序列、映射、基本數據類型以及類實例構成。 模式匹配使得程序能夠從復雜的數據類型中提取信息、根據數據結構實現分支&#xff0c;并基于不同的數據形式應用特定的動作。 語法與操作 模…

Linux下配置Pytorch

1.Anaconda 1.1虛擬環境創建 2.Nvidia驅動 3.CUDA驅動安裝 4.Pytorch安裝 具體的步驟如上&#xff1a;可參考另一位博主的博客非常詳細&#xff1a; Linux服務器配置PythonPyTorchCUDA深度學習環境_linux cuda環境配置-CSDN博客https://blog.csdn.net/NSJim/article/detai…

極海APM32F072用Keil5燒錄失敗Error: Flash Download failed -“Cortex-MO+“

在用Keil5燒錄時&#xff0c;出現錯誤彈窗&#xff0c;大概長這樣&#xff1a; 檢查了一圈設置&#xff0c;都搞不好。 先用J-Flash&#xff0c;顯示讀寫保護&#xff08;未截圖&#xff09;&#xff0c;會跳出界面讓選擇是否解除讀寫保護&#xff1a; 1.點擊允許讀操作YES&am…

DNF手游攻略:0氪攻略,轉職技巧與避坑指南!

在DNF手游的冒險旅程中&#xff0c;角色的轉職是一次重要的成長經歷。通過轉職&#xff0c;玩家可以獲得全新的技能和屬性&#xff0c;提升自己在地下城中的戰斗力。本文將為您介紹轉職后的關鍵技巧和日常任務&#xff0c;幫助您更好地適應新的職業身份&#xff0c;成為地下城中…

Python從0到100(二十九):requests模塊處理cookie

1 爬蟲中使用cookie 為了能夠通過爬蟲獲取到登錄后的頁面&#xff0c;或者是解決通過cookie的反扒&#xff0c;需要使用request來處理cookie相關的請求 1.1 爬蟲中使用cookie的利弊 帶上cookie的好處 能夠訪問登錄后的頁面能夠實現部分反反爬 帶上cookie的壞處 一套cookie往往…

數據庫與低代碼開發:技術革新與應用實踐

在數字化時代&#xff0c;企業對軟件開發的需求日益增長&#xff0c;同時對開發效率和成本控制的要求也越來越高。在這樣的背景下&#xff0c;低代碼開發平臺應運而生&#xff0c;它允許開發者通過圖形界面和配置化操作&#xff0c;快速構建應用程序&#xff0c;而無需編寫大量…

【設計模式】JAVA Design Patterns——Monitor(監視器模式)

&#x1f50d;目的 主要目的是為多個線程或進程提供一種結構化和受控的方式來安全地訪問和操作共享資源&#xff0c;例如變量、數據結構或代碼的關鍵部分&#xff0c;而不會導致沖突或競爭條件。 &#x1f50d;解釋 通俗描述 監視器模式用于強制對數據進行單線程訪問。 一次只允…

Windows線程同步的四種方式和區別

1. Windows線程同步的四種方式 2. 區別 Critical Section更多強調的是保護&#xff0c;Event對象、Mutex對象與Semaphore對象更多的強調的是同步&#xff1b;Critical Section對象是無法設置等待超時的&#xff0c;而其他三個對象則可以設置等待超時&#xff0c;從這一點來講…

ROS2在RVIZ2中加載機器人urdf模型

參考ROS2-rviz2顯示模型 我這邊用的solid works生成的urdf以及meshes&#xff0c;比參考的方法多了meshes 問題一&#xff1a;Error retrieving file [package://rm_dcr_description/meshes/leftarm_link7.STL]: Package [rm_dcr_description] does not exist 這個是urdf模型中…

VisualStudio中:如果某個項目不顯示SVN的show log等,而其他項目都正常

VisualStudio中&#xff1a;如果某個項目不顯示SVN的show log等&#xff0c;而其他項目都正常。說明大概率是當前項目的問題&#xff0c;而不是VisualStudio的問題&#xff01; 1.這個項目內有一個“隱藏”文件夾.svn 》先刪除&#xff01; 2.如果外層文件夾有紅色感嘆號&…

2024-5-14——完成所有任務需要的最少輪數

2024-5-14 題目來源我的題解方法一 哈希表數學 題目來源 力扣每日一題&#xff1b;題序&#xff1a;2244 我的題解 方法一 哈希表數學 根據數學規律可以發現&#xff0c;除了只有1個任務時不能完成任務&#xff0c;其他的都可以完成。并且需要的輪數為&#xff1a; ?x/3? …

16、matlab求導、求偏導、求定積分、不定積分、數值積分和數值二重積分

1、matlab求導,diff()函數 1)一階導數 語法:diff(f(x)):求一階導數 //diff(f(x),n):求n階導數(n為具體正整數) 以函數(cos(x)+sin(x)-x^2)的一階導數為例 一階導數代碼: yms x;%聲明符號變量x f(x)=cos(x)+sin(x)-x^2;%定義原式子 dy=diff(f(x))%求一階導數dy =cos(…

機器學習-12-開源的機器學習可視化拖拉拽工具orange3的應用

參考orange3,一個無敵的 Python 庫! 參考orange3的GitHub地址 參考orange3的官方應用示例 參考下載Orange模塊,總是出錯? 參考Orange3入門(Orange3Dev) 參考工具|Orange 3:機器學習入門神器 1 orange3 數據科學和機器學習是當今科技領域的重要組成部分,而數據分析和建…

Csv--01--ExportUtil 寫文件

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄 ExportUtil案例&#xff1a; ExportUtil.writeCsvFile ExportUtil import com.alibaba.fastjson.JSON; import com.google.common.collect.Lists;import org.apache…

記錄request中上傳圖片接口的測試過程

F12抓取的接口地址如下&#xff1a; 入參&#xff1a; 注意圈出來的部分參數 apifox調試接口 python相關代碼&#xff1a; 接口調用相關文件&#xff1a; select_photo.py 輸出結果&#xff1a; 相關接口的一些封裝&#xff1a;

Window 批量改文件后綴,例如,test.xls文件改為 test.csv;或者sas 文件改 sql 文件

echo off setlocal enabledelayedexpansion :: 設置需要遍歷的文件夾路徑 set "folderPathD:\test" ::遍歷文件夾內所有文件夾 for /d %%i in("folderPath%\*") do( echo Processing folder: %%i ::遍歷當前文件夾內所有.sas文件 for %%j in ("%%i\*…