緩存的讀寫

緩存作為一種高速交換數(shù)據(jù)的存儲器, 其目的是為了協(xié)調(diào)兩種硬件之間的傳輸差異或者避免多次重復計算耗時任務,.

緩存無處不在(無論是硬件還是軟件方面), 目前大熱的Redis數(shù)據(jù)庫就是緩存的一種實現(xiàn), 但除此之外, 還有很多的實現(xiàn)方式.

但是請注意一句真理: 請不要過早的優(yōu)化結構. 緩存是個香餑餑的東西, 但是過早的引用, 會導致大量的問題. 對于互聯(lián)網(wǎng)App來說, 我認為使用的緩存的前提是應用扛不住用戶的并發(fā)量時

緩存的讀寫

既然是緩存, 那就一定還會有實際的數(shù)據(jù)端, 比如數(shù)據(jù)庫. 當我們進行讀寫時, 就要考慮到兩者的讀寫情況, 因為我們不能把緩存和數(shù)據(jù)庫當作一個事務內(nèi)的操作(盡管可以, 但性能會大幅度的下降)

對于緩存的讀寫, 產(chǎn)生3種機制:

  • Cache Asiden Pattern
  • Write/Read Through
  • Write Back

Cache Asiden Pattern

這個機制下, 對于讀, 我們是這么描述的

  • 應用端發(fā)起讀請求, 查找緩存是否存在, 若存在, 命中且返回
  • 緩存不存在, 應用端向數(shù)據(jù)庫發(fā)起請求, 獲取數(shù)據(jù), 并同時修改到緩存中

對于寫, 涉及到更新, 有4種可能的方案

  • 更新緩存, 再更新數(shù)據(jù)庫
  • 更新數(shù)據(jù)庫, 再更新緩存
  • 刪除緩存, 再更新數(shù)據(jù)庫
  • 更新數(shù)據(jù)庫, 再刪除庫

對于第一種, 在多線程的情況下:

  • 假設A線程將key為cache的值更新為2, 先更新緩存, 此時線程切換
  • 線程B將key為cache的值更新為3, 更新緩存成功后接著更新數(shù)據(jù)庫, 此時數(shù)據(jù)庫中值為3
  • B完成后, A切換回來, 更新數(shù)據(jù)庫, 改成2. 數(shù)據(jù)丟失

第二種跟第一種差不多, 會造成數(shù)據(jù)不一致的問題. 出現(xiàn)的原因在于, 我們要同時更新兩個數(shù)據(jù)庫, 然后我們并沒有任何機制去保證它們的原子性, 當線程切換時, 就會產(chǎn)生意想不到的結果. 除此之外, 在某些數(shù)據(jù)不停被修改的場景下, 我們需要頻繁的覆蓋緩存值, 這是不合理的

解決方案是: 我們先更新數(shù)據(jù)庫,然后刪除緩存, 當相應的讀請求到來時, 再從數(shù)據(jù)庫讀取

第三種為什么也不行呢?

  • 假設線程A要將k ey為cache的值更新為2, 先刪除緩存, 此時線程切換
  • 線程B要讀取cache的值, 發(fā)現(xiàn)緩存中沒有, 讀取數(shù)據(jù)庫, 此時還為舊值
  • 線程A更新數(shù)據(jù)庫, 數(shù)據(jù)產(chǎn)生不一致

第四種就完美了嗎?

  • 假設線程A要讀取一個緩存中不存在的值, 向數(shù)據(jù)庫讀取, 在準備將該值寫入緩存時, 線程切換
  • 線程B更新該值, 寫入數(shù)據(jù)庫, 刪除緩存中值.
  • 線程A寫入緩存, 此時數(shù)據(jù)不一致

這樣一看, 既然第四種還有問題, 為什么人們還推薦使用? 因為這個case只有在理論上才會出現(xiàn), 原因是它的發(fā)生要剛好在緩存值失效的同時, 且有個并發(fā)寫請求, 并且讀所消耗的時間, 要比寫大(有點異想天開).

總結一下: Cashe Aside流程

  • 對于讀, 緩存在, 命中返回. 不存在, 從數(shù)據(jù)庫讀取, 并更新到緩存上
  • 對于寫, 先更新數(shù)據(jù)庫, 在更新緩存

補充幾個問題

  1. 有沒有別的場景適合使用其他寫方案?
    比如用戶注冊后需要顯示用戶資料, 所以按照上文機制, 更新完數(shù)據(jù)庫, 刪除緩存(沒有信息). 然后接下來的讀請求, 因為讀寫分離, 在讀從庫的時候很有可能因為讀寫延遲, 而導致讀出的數(shù)據(jù)為空
    針對這個場景, 我們想一想. 注冊用戶可能是不存在并發(fā)的, 如果我們是用更新數(shù)據(jù)庫, 在更新緩存的策略, 是不是能完美解決讀寫延遲問題

  2. 緩存對數(shù)據(jù)的態(tài)度是, 盡可能“保持數(shù)據(jù)一致性”, 也就是最終一致性. 那有什么辦法, 能強行保證一致性嗎?
    使用更新數(shù)據(jù)庫, 在更新緩存的方案, 同時設置分布式鎖, 對于并發(fā)同一熱點的數(shù)據(jù)上鎖

Read/Write Through

image.png

它的核心在于: Cache Aside 模式是應用分別和緩存, 數(shù)據(jù)庫打交道. 而Read/Write Through是緩存替應用來打交到

  • 對于讀請求, 命中返回數(shù)據(jù). 未命中, 緩存去向數(shù)據(jù)庫索取數(shù)據(jù), 并存放到緩存中后, 返回
  • 對于寫請求, 命中. 更新緩存, 再由緩存去更新數(shù)據(jù)庫(這是個同步操作)
  • 若寫未命中, 應用直接更新數(shù)據(jù)庫(減少一次寫入, 提升寫性能)

注意Redis, Memcached不提供與數(shù)據(jù)庫打交道, 或自動加載數(shù)據(jù)庫的功能. 所以分布式緩存一般使用的是Cache Aside模式, 而一般的本地緩存(Guava Cache)則使用的是Read/Write Through模式

但是對于寫機制來說, 如果緩存命中, 每次都要同步更新數(shù)據(jù)庫, 造成的延遲會比較高. 有沒有什么異步寫入的辦法?

Write Back

image.png

它的思想在于, 我們不需要每次進來寫請求, 就更新數(shù)據(jù)庫, 磁盤. 同時將它們攢起來, 做一些合并操作, 待到一個時機再一起寫入. (能大大的提升I/O性能)

它的弊端在于, 攢這個過程如果電腦突然掉電, 那數(shù)據(jù)就都沒了.

寫回機制在Unix系統(tǒng)上很多地方都用到, 比如人們熟知的“page cache”, 或者 CPU 與 L1,L2,L3緩存的寫入機制. CPU寫入時, 會把數(shù)據(jù)只寫到緩存中, 當該行數(shù)據(jù)需要被替換出去時, 才會正式寫入內(nèi)存.

總結

緩存并不是這幾年才流行的產(chǎn)物, 它的應用隨處可見. 理解了緩存, 你就擁有了互聯(lián)網(wǎng)開發(fā)三大神奇之一, 其他兩個為(MQ和RPC)

本篇中我們圍繞著緩存的讀寫, 引出了幾種機制. 而最近大火的Redis和Memcached使用的都是Cache Aside. Cache Aside的主要問題是因為并發(fā),而產(chǎn)生的不確定性. 所以在使用的時候, 圍繞著并發(fā)和你的應用場景去選擇才是最好的.

在理解緩存中, 心中一定要有個trade-off, 即數(shù)據(jù)一致性和 查詢性能的選擇.

參考: 緩存更新的套路

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末适荣,一起剝皮案震驚了整個濱河市梳侨,隨后出現(xiàn)的幾起案子撇吞,更是在濱河造成了極大的恐慌崩泡,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嘀倒,死亡現(xiàn)場離奇詭異,居然都是意外死亡躺屁,警方通過查閱死者的電腦和手機树姨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來闺兢,“玉大人茂缚,你說我怎么就攤上這事∥萏罚” “怎么了脚囊?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長桐磁。 經(jīng)常有香客問我悔耘,道長,這世上最難降的妖魔是什么我擂? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任衬以,我火速辦了婚禮,結果婚禮上校摩,老公的妹妹穿的比我還像新娘看峻。我一直安慰自己,他們只是感情好衙吩,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布互妓。 她就那樣靜靜地躺著,像睡著了一般坤塞。 火紅的嫁衣襯著肌膚如雪冯勉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天尺锚,我揣著相機與錄音珠闰,去河邊找鬼。 笑死瘫辩,一個胖子當著我的面吹牛伏嗜,可吹牛的內(nèi)容都是我干的坛悉。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼承绸,長吁一口氣:“原來是場噩夢啊……” “哼裸影!你這毒婦竟也來了?” 一聲冷哼從身側響起军熏,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤轩猩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后荡澎,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體均践,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年摩幔,在試婚紗的時候發(fā)現(xiàn)自己被綠了彤委。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡或衡,死狀恐怖焦影,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情封断,我是刑警寧澤斯辰,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站坡疼,受9級特大地震影響彬呻,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜柄瑰,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一废岂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧狱意,春花似錦湖苞、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至藏姐,卻和暖如春隆箩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背羔杨。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工捌臊, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人兜材。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓理澎,卻偏偏與公主長得像逞力,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子糠爬,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345