在項目中緩存是經常用到的,為了減少和數據庫的交互,小伙伴們利用緩存的思路如下:
成都創(chuàng)新互聯公司是一家專業(yè)從事做網站、網站設計、網頁設計的品牌網絡公司。如今是成都地區(qū)具影響力的網站設計公司,作為專業(yè)的成都網站建設公司,成都創(chuàng)新互聯公司依托強大的技術實力、以及多年的網站運營經驗,為您提供專業(yè)的成都網站建設、營銷型網站建設及網站設計開發(fā)服務!
緩存設計思路
我們小伙伴們有沒有考慮到緩存更新的問題,小伙伴們肯定會說肯定用過啊,有數據更新時,把緩存清空掉就行了啊,下一次訪問的時候服務就會把新值設置到緩存中了。這樣不就行了嗎?對的,在一般項目中,這樣的使用就夠了。那老顧帶著大家看看在高并發(fā)場景下,會有什么問題?
我們舉例說明,就拿商品的庫存作為緩存。那現在我們要更新緩存中的庫存值,怎么進行操作,我們看下面幾個場景:
存在的問題場景:請求A更新值為99,請求B更新值為98
上圖流程:
這樣數據庫的值為98,但緩存的值為99,數值不一致。(不推薦)
這個流程跟上面很類似,出現的問題也很類似
這樣就緩存的值為98,數據庫為99導致不一致。(不推薦)
存在的問題場景:請求A更新值為99,請求B獲取值
上圖中請求流程:
這樣就導致了緩存和數據庫的不一致問題,緩存中的值一直是舊數據。(不推薦)
這個方案也是老外提出的《Cache-Aside pattern》更新緩存的策略。這種策略先保證了源頭的數據一定是正確的。這種策略是不是萬無一失呢,有一種非常特殊的場景
上圖流程:建立中緩存突然失效了
這種情況發(fā)生的不一致,是因為緩存突然失效了。而且還要保證請求B更新操作 比 請求A的查詢操作還要快;才會導致不一致。這種情況概率會很少。一般要求不高的項目可以采用此方式(推薦)。
這種先更新數據庫,再刪除緩存的策略中,因為要刪除緩存,但如果緩存刪除失敗,就會導致數據庫與緩存不一致。這個問題怎么辦?我們正常想到的是利用我們MQ中間件去實現。
上圖的流程,如果刪除緩存失敗,發(fā)送消息投遞到消息中間件中,進入消息隊列。也許有小伙伴就會問,如果消息投遞失敗怎么辦?我們可以利用消息中間件那邊的保證100%消息投遞的機制(這個以后再講)。這樣就保證了即使刪除消息失敗,我們也會重試。
不過這個方案有個問題,就是和我們應用服務的業(yè)務代碼耦合的比較厲害。代碼業(yè)務不清晰。
那我們有沒有別的方案呢,對業(yè)務沒有侵入呢?
上圖中其實是利用了MySQL的底層機制,binlog日志進行刪除緩存,這樣就不需要和業(yè)務關聯,刪除緩存服務是獨立的。我們可以利用阿里開源的canal去操作。
這種先更新數據庫,再刪除緩存的策略是不是就沒有問題呢?我們來看一下另一個場景,數據庫的讀寫分離的場景。一般中大型項目都會用到數據庫的讀寫分離。寫請求在一個庫,讀請求在另一個庫。讀寫分離會有個問題,就是庫與庫之間會存在數據延遲,因為存在數據同步。
那我們再看一下上面的場景流程,就會有問題,因為請求B更新數據 在一個庫上面,請求A去讀取數據時是另一個庫。
這樣就導致不一致,這個場景是經常出現的,不是小概率事件。那我們如何處理呢?老顧下次再介紹。
總結:整個導致不一致的原因就是因為高并發(fā)情況下各個請求執(zhí)行的順序是無法確定的,不知道哪個請求先執(zhí)行,哪個后執(zhí)行導致。