數據庫緩存最終一致性的四種方案

背景

緩存是軟件開發中一個很是有用的概念,數據庫緩存更是在項目中必然會遇到的場景。而緩存一致性的保證,更是在面試中被反覆問到,這裏進行一下總結,針對不一樣的要求,選擇恰到好處的一致性方案。mysql

緩存是什麼

存儲的速度是有區別的。緩存就是把低速存儲的結果,臨時保存在高速存儲的技術。面試

如圖所示,金字塔更上面的存儲,能夠做爲下面存儲的緩存。

咱們本次的討論,主要針對數據庫緩存場景,將以redis做爲mysql的緩存爲案例來進行。redis

爲何須要緩存

存儲如mysql一般支持完整的ACID特性,由於可靠性,持久性等因素,性能廣泛不高,高併發的查詢會給mysql帶來壓力,形成數據庫系統的不穩定。同時也容易產生延遲。根據局部性原理,80%請求會落到20%的熱點數據上,在讀多寫少場景,增長一層緩存很是有助提高系統吞吐量和健壯性。sql

存在問題

存儲的數據隨着時間可能會發生變化,而緩存中的數據就會不一致。具體能容忍的不一致時間,須要具體業務具體分析,可是一般的業務,都須要作到最終一致。數據庫

redis做爲mysql緩存

一般的開發模式中,都會使用mysql做爲存儲,而redis做爲緩存,加速和保護mysql。可是,當mysql數據更新以後,redis怎麼保持同步呢。緩存

強一致性同步成本過高,若是追求強一致,那麼不必用緩存了,直接用mysql便可。一般考慮的,都是最終一致性。服務器

解決方案

方案一

經過key的過時時間,mysql更新時,redis不更新。 這種方式實現簡單,但不一致的時間會很長。若是讀請求很是頻繁,且過時時間比較長,則會產生不少長期的髒數據。併發

優勢:異步

  • 開發成本低,易於實現;
  • 管理成本低,出問題的機率會比較小。

不足高併發

  • 徹底依賴過時時間,時間過短容易緩存頻繁失效,太長容易有長時間更新延遲(不一致)

方案二

在方案一的基礎上擴展,經過key的過時時間兜底,而且,在更新mysql時,同時更新redis。

同時更新redis

優勢

  • 相對方案一,更新延遲更小。

不足

  • 若是更新mysql成功,更新redis卻失敗,就退化到了方案一;
  • 在高併發場景,業務server須要和mysql,redis同時進行鏈接。這樣是損耗雙倍的鏈接資源,容易形成鏈接數過多的問題。

方案三

針對方案二的同步寫redis進行優化,增長消息隊列,將redis更新操做交給kafka,由消息隊列保證可靠性,再搭建一個消費服務,來異步更新redis。

優勢

  • 消息隊列能夠用一個句柄,不少消息隊列客戶端還支持本地緩存發送,有效解決了方案二鏈接數過多的問題;
  • 使用消息隊列,實現了邏輯上的解耦;
  • 消息隊列自己具備可靠性,經過手動提交等手段,能夠至少一次消費到redis。

不足

  • 依舊解決不了時序性問題,若是多臺業務服務器分別處理針對同一行數據的兩條請求,舉個栗子,a = 1; a = 5;,若是mysql中是第一條先執行,而進入kafka的順序是第二條先執行,那麼數據就會產生不一致。
  • 引入了消息隊列,同時要增長服務消費消息,成本較高。

方案四

經過訂閱binlog來更新redis,把咱們搭建的消費服務,做爲mysql的一個slave,訂閱binlog,解析出更新內容,再更新到redis。

優勢

  • 在mysql壓力不大狀況下,延遲較低;
  • 和業務徹底解耦;
  • 解決了時序性問題。

缺點

  • 要單獨搭建一個同步服務,而且引入binlog同步機制,成本較大。

總結

方案選型

  1. 首先確認產品上對延遲性的要求,若是要求極高,且數據有可能變化,別用緩存。
  2. 一般來講,方案1就夠了,筆者諮詢過4,5個團隊,基本都是用方案1,由於能用緩存方案,一般是讀多寫少場景,同時業務上對延遲具備必定的包容性。方案1沒有開發成本,其實比較實用。
  3. 若是想增長更新時的即時性,就選擇方案2,不過不必作重試保證之類的。
  4. 方案3,方案4針對於對延時要求比較高業務,一個是推模式,一個是拉模式,而方案4具有更強的可靠性,既然都願意花功夫作處理消息的邏輯,不如一步到位,用方案4。

結論

通常狀況,方案1夠用。若延時要求高,直接選擇方案4。若是是面試場景,從簡單講到複雜,面試官會一步一步追問,我們就一點點推導,賓主盡歡。

相關文章
相關標籤/搜索