Redis 資料持久化

2021-07-11 22:54:21 字數 3407 閱讀 3448

首先redis內部使用乙個redisobject物件來表示所有的key和value,redisobject最主要的資訊如上圖所示:type代表乙個value物件具體是何種資料型別,encoding是不同資料型別在redis內部的儲存方式,比如:type=string代表value儲存的是乙個普通字串,那麼對應的encoding可以是raw或者是int,如果是int則代表實際redis內部是按數值型類儲存和表示這個字串的,當然前提是這個字串本身可以用數值表示,比如:」123」 「456」這樣的字串。

string

實現方式:

string在redis內部儲存預設就是乙個字串,被redisobject所引用,當遇到incr,decr等操作時會轉成數值型進行計算,此時redisobject的encoding欄位為int。

hash

redis提供了介面(hgetall)可以直接取到全部的屬性資料,但是如果內部map的成員很多,那麼涉及到遍歷整個內部map的操作,由於**redis單執行緒模型**的緣故,這個遍歷操作可能會比較耗時,而另其它客戶端的請求完全不響應,這點需要格外注意。

實現方式:

上面已經說到redis hash對應value內部實際就是乙個hashmap,實際這裡會有2種不同實現,這個hash的成員比較少時redis為了節省記憶體會採用類似一維陣列的方式來緊湊儲存,而不會採用真正的hashmap結構,對應的value redisobject的encoding為zipmap,當成員數量增大時會自動轉成真正的hashmap,此時encoding為ht。

list

實現方式:

redis list的實現為乙個**雙向鍊錶**,即可以支援反向查詢和遍歷,更方便操作,不過帶來了部分額外的記憶體開銷,redis內部的很多實現,包括傳送緩衝佇列等也都是用的這個資料結構。

set實現方式:

set 的內部實現是乙個 value永遠為null的**hashmap**,實際就是通過計算hash的方式來快速排重的,這也是set能提供判斷乙個成員是否在集合內的原因。

sorted set

實現方式:

redis sorted set的**內部使用hashmap和跳躍表(skiplist)**來保證資料的儲存和有序,hashmap裡放的是成員到score的對映,而跳躍表裡存放的是所有的成員,排序依據是hashmap裡存的score,使用跳躍表的結構可以獲得比較高的查詢效率,並且在實現上比較簡單

最後想說的是redis內部實現沒有對記憶體分配方面做過多的優化,在一定程度上會存在記憶體碎片,不過大多數情況下這個不會成為redis的效能瓶頸,不過如果在redis內部儲存的大部分資料是數值型的話,redis內部採用了乙個shared integer的方式來省去分配記憶體的開銷,即在系統啟動時先分配乙個從1~n 那麼多個數值物件放在乙個池子中,如果儲存的資料恰好是這個數值範圍內的資料,則直接從池子裡取出該物件,並且通過引用計數的方式來共享,這樣在系統儲存了大量數值下,也能一定程度上節省記憶體並且提高效能,這個引數值n的設定需要修改源**中的一行巨集定義redis_shared_integers,該值預設是10000,可以根據自己的需要進行修改,修改後重新編譯就可以了。redis由於支援非常豐富的記憶體資料結構型別,如何把這些複雜的記憶體組織方式持久化到磁碟上是乙個難題,所以redis的持久化方式與傳統資料庫的方式有比較多的差別,redis一共支援四種持久化方式,分別是:

定時快照方式(snapshot)

基於語句追加檔案的方式(aof)

虛擬記憶體(vm)

diskstore方式

在設計思路上,前兩種是基於全部資料都在記憶體中,即小資料量下提供磁碟落地功能,而後兩種方式則是作者在嘗試儲存資料超過物理記憶體時,即大資料量的資料儲存,截止到本文,後兩種持久化方式仍然是在實驗階段,並且vm方式基本已經被作者放棄,所以實際能在生產環境用的只有前兩種,換句話說redis目前還只能作為小資料量儲存(全部資料能夠載入在記憶體中),海量資料儲存方面並不是redis所擅長的領域。下面分別介紹下這幾種持久化方式:

該持久化方式實際是在redis內部乙個定時器事件,每隔固定時間去檢查當前資料發生的改變次數與時間是否滿足配置的持久化觸發的條件,如果滿足則通過作業系統fork呼叫來建立出乙個子程序,這個子程序缺省會與父程序共享相同的位址空間,這時就可以通過子程序來遍歷整個記憶體來進行儲存操作,而主程序則仍然可以提供服務,當有寫入時由作業系統按照記憶體頁(page)為單位來進行copy-on-write保證父子程序之間不會互相影響

該持久化的主要缺點是定時快照只是代表一段時間內的記憶體映像,所以系統重啟會丟失上次快照與重啟之間所有的資料。

aof方式實際類似mysql的基於語句的binlog方式,即每條會使redis記憶體資料發生改變的命令都會追加到乙個log檔案中,也就是說這個log檔案就是redis的持久化資料

aof的方式的主要缺點是追加log檔案可能導致體積過大,當系統重啟恢復資料時如果是aof的方式則載入資料會非常慢,幾十g的資料可能需要幾小時才能載入完,當然這個耗時並不是因為磁碟檔案讀取速度慢,而是由於讀取的所有命令都要在記憶體中執行一遍。另外由於每條命令都要寫log,所以使用aof的方式,redis的讀寫效能也會有所下降

虛擬記憶體方式是redis來進行使用者空間的資料換入換出的乙個策略,此種方式在實現的效果上比較差,主要問題是**複雜,重啟慢,複製慢等等,目前已經被作者放棄。

diskstore方式是作者放棄了虛擬記憶體方式後選擇的一種新的實現方式,也就是傳統的b-tree的方式,目前仍在實驗階段,後續是否可用我們可以拭目以待。

有redis線上運維經驗的人會發現redis在物理記憶體使用比較多,但還沒有超過實際物理記憶體總容量時就會發生不穩定甚至崩潰的問題,有人認為是基於快照方式持久化的fork系統呼叫造成記憶體占用加倍而導致的,這種觀點是不準確的,因為fork 呼叫的copy-on-write機制是基於作業系統頁這個單位的,也就是只有有寫入的髒頁會被複製,但是一般你的系統不會在短時間內所有的頁都發生了寫入而導致複製,那麼是什麼原因導致redis崩潰的呢?

答案是redis的持久化使用了buffer io造成的,所謂buffer io是指redis對持久化檔案的寫入和讀取操作都會使用物理記憶體的page cache,而大多數資料庫系統會使用direct io來繞過這層page cache並自行維護乙個資料的cache,而當redis的持久化檔案過大(尤其是快照檔案),並對其進行讀寫時,磁碟檔案中的資料都會被載入到物理記憶體中作為作業系統對該檔案的一層cache,而這層cache的資料與redis記憶體中管理的資料實際是重複儲存的,雖然核心在物理記憶體緊張時會做page cache的剔除工作,但核心很可能認為某塊page cache更重要,而讓你的程序開始swap ,這時你的系統就會開始出現不穩定或者崩潰了。我們的經驗是當你的redis物理記憶體使用超過記憶體總容量的3/5時就會開始比較危險了。

redis持久化資料

如果我們想要redis僅僅作為純記憶體的快取來用,那麼可以禁止rdb和aof所有的持久化機制 如果同時使用rdb和aof兩種持久化機制,那麼在redis重啟的時候,會使用aof來重新構建資料,因為aof中的資料更加完整 1 rdb會生成多個資料檔案,每個資料檔案都代表了某乙個時刻中redis的資料,...

redis資料持久化RDBAOF

全稱 reids database 資料持久化方式之一 在指定時間間隔內,將記憶體中的資料集快照寫入硬碟。術語叫 snapshot 快照。恢復時,將快照檔案直接讀到記憶體裡。root host53 cd var lib redis 6379 root host53 6379 ls dump.rdb ...

Redis資料持久化 RDB AOF

一 redis持久化的兩種方式 記憶體快照,將資料庫中的key value以二進位制的方式存放在rdb檔案中,速度快,但是會丟失事務開始到結束的部分。資料增刪改的日誌,每條日誌插入到aof檔案末尾,資料齊全,但是檔案龐大,恢復慢。二 相輔相成 因為rdb是在指定時間 指定次數後才做快照,並非實時,可...