首頁  >  文章  >  資料庫  >  Redis快取問題怎麼解決

Redis快取問題怎麼解決

PHPz
PHPz轉載
2023-06-03 17:56:411161瀏覽

LevelDB 來了!

這是一個由 Google 開源的 NOSQL 儲存引擎庫,是現代分散式儲存領域不可或缺的利器。在它的基礎之上,Facebook 開發了另一個 NOSQL 儲存引擎庫 RocksDB,沿用了 LevelDB 的先進技術架構的同時也解決了 LevelDB 的一些短板。你可以將 RocksDB 比喻成氫彈,它比 LevelDB 的威力更大一些。許多現代開源資料庫採用了 RocksDB 作為底層儲存引擎,TiDB 就是其中一個著名的例子。

但為什麼我要講 LevelDB 而不是 RocksDB 呢?原因在於 LevelDB 技術架構更簡單清晰易於理解。如果我們先把 LevelDB 吃透了再去啃一啃 RocksDB 就會非常好懂了,RocksDB 也只是在 LevelDB 的基礎上添磚加瓦進行了一系列優化而已。當我們打破 RocksDB 這個難題時,TiDB 的核動力太空船已經在前方不遠處迎接我們了。

Redis 快取有什麼問題?

當我們使用 Redis 作為快取時,通常仍會有一個持久性資料庫記錄完整的冷、熱資料。 Redis 和持久層資料庫之間的資料一致性是由應用程式自己來控制的。當快取中沒有資料時,應用程式需要從持久層載入資料並將其放入快取中。當資料更新發生時,需要將快取置為失效。

<code class="hljs javascript">function getUser(String userId) User {<br>  User user = redis.get(userId);<br>  if user == null {<br>    user = db.get(userId);<br>    if user != null {<br>      redis.set(userId, user);<br>    }<br>  }<br>  return user;<br>}<br><br>function updateUser(String userId, User user) {<br>  db.update(userId, user);<br>  redis.expire(userId);<br>}<br></code>


有過這方面開發經驗的朋友們就知道寫這樣的程式碼還是挺繁瑣的,所有的涉及到快取的業務程式碼都需要加上這一部分邏輯。

嚴格來說我們還需要仔細考慮快取一致性問題,例如在updateUser 方法中,資料庫正確執行了更新,但是快取redis 因為網路抖動等原因置為失效沒有成功,那麼快取中的數據就成了過期數據。讀者可以考慮,即使反轉設定快取和更新持久的順序,仍可能出現其他問題。

在多進程高並發場合也會導致快取不一致,例如一個行程對某個 userId 呼叫 getUser() 方法,因為快取裡沒有,它需要從資料庫載入。結果剛剛加載出來,正準備要設定緩存,這時候發生了內存fullgc 代碼暫停了一會,而正在此時另一個進程調用了updateUser 方法更新了數據庫,將緩存置為失效(其實緩存裡本來就沒有數據)。然後前面那個行程終於 fullgc 結束要開始設定快取了,這時候進入快取的就是過期的資料。

LevelDB 是如何解決的?

LevelDB 將 Redis 快取和持久層合而為一,一次幫你搞定快取和持久層。有了 LevelDB,你的程式碼可以簡化成下面這樣

<code class="hljs javascript">function getUser(String userId) User {<br>  return leveldb.get(userId);<br>}<br><br>function updateUser(String userId, User user) {<br>  leveldb.set(userId, user);<br>}<br></code>


而且你再也不用當心緩存一致性問題了,LevelDB 的資料更新要麼成功要麼不成功,不存在中間薛定諤狀態。用戶無需關注資料一致性的細節,因為LevelDB內部整合了記憶體快取和持久層磁碟檔案。

LevelDB 具體是什麼?

我們之前提到它是一種非關聯式儲存引擎,和 Redis 的概念不同。 Redis 是一個完整的資料庫,而 LevelDB 它只是一個引擎。如果將資料庫比喻為高級跑車,則儲存引擎便是其發動機,也即是它的核心和心臟。有了這個發動機,我們再給它包裝上一系列的配件和裝飾,就可以成為資料庫。不過也不要小瞧了配件和裝飾,做到極致那也是非常困難,將 LevelDB 包裝成一個簡單易用的資料庫需要加上太多太多精緻的配件。 LevelDB 和 RocksDB 出來這麼多年,能夠在它的基礎上做出非常一個完備的生產級資料庫寥寥無幾。

LevelDB可以被視為一個記憶體中的鍵值資料庫。它提供了基礎的 Get/Set API,我們在程式碼裡可以透過這個 API 來讀寫資料。你也可以將它看成一個無限大小的高級 HashMap,我們可以往裡面塞入無限條 Key/Value 數據,只要磁碟可以裝下。

由於它只能視為一種記憶體資料庫,其中儲存的資料無法在進程或機器間共用。在分散式領域,LevelDB 要如何大顯身手呢?

要實現這一點,需要將網路 API 包裝在 LevelDB 記憶體資料庫之上。當多個進程位於不同的機器上並希望存取該資源時,它們都必須透過網路 API 介面進行統一存取。這樣就形成了一個簡易的資料庫。套用 Redis 協定封裝網路層,即可使用 Redis 用戶端對資料庫進行讀寫操作。

如果要考慮資料庫的高可用性,我們在上面這個單機資料庫的基礎上再加上主從複製功能就可以變身成為一個主從結構的分散式 NOSQL 資料庫。在主從資料庫前面加上一層轉發代理(負載平衡器如 LVS、F5 等),就可以實現主從的即時切換。

如果你需要的資料容量特別大以至於單一機器的硬碟都容不下,這時候就需要資料分片機制將整個資料庫的資料分散到多台機器上,每台機器只負責一部分資料的讀寫工作。資料分片的方案非常多,可以像Codis 一樣透過轉送代理來分片,也可以像Redis-Cluster 一樣使用客戶端轉送機制來分片,也可以使用TiDB 的Raft 分散式一致性演算法來分組管理分片。最簡單最容易理解的還是要數 Codis 的轉發代理分片。

當資料量持續成長需要新增節點時,就必須將舊節點上的資料部分遷移到新節點上,管理資料的均衡和遷移的又是一個新的高階配件- 資料均衡器。

以上是Redis快取問題怎麼解決的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:yisu.com。如有侵權,請聯絡admin@php.cn刪除