一:快照模式
預設redis是會以快照的形式將資料持久化到磁碟的(一個二進位制檔案,dump.rdb,這個檔名字可以指定),在配置檔案中的格式是:save N M表示在N秒之內,redis至少發生M次修改則redis抓快照到磁碟。當然我們也可以手動執行save或者bgsave(非同步)做快照。
工作原理簡單介紹一下:當redis需要做持久化時,redis會fork一個子程序;子程序將資料寫到磁碟上一個臨時RDB檔案中;當子程序完成寫臨時檔案後,將原來的RDB替換掉,這樣的好處就是可以copy-on-write
如下圖:
上面三組命令也是非常好理解的,就是說900指的是“秒數”,1指的是“change次數”,接下來如果在“900s“內至少有1次更改,那麼就執行save儲存,同樣
的道理,如果300s內至少有10次change,60s內至少有1w次change,那麼也會執行save操作。
以上是redis自身進行的同步操作。
下面是我們可以以手工執行save
redis提供了兩個操作命令:save,bgsave,這兩個命令都會強制將資料重新整理到硬碟中,如下圖:
RDB 的優點:
RDB 是一個非常緊湊(compact)的檔案,它儲存了 Redis 在某個時間點上的資料集。 這種檔案非常適合用於進行備份: 比如說,你可以在最近的 24 小時內,每小時備份一次 RDB 檔案,並且在每個月的每一天,也備份一個 RDB 檔案。
這樣的話,即使遇上問題,也可以隨時將資料集還原到不同的版本。RDB 非常適用於災難恢復(disaster recovery):它只有一個檔案,並且內容都非常緊湊,可以(在加密後)將它傳送到別的資料中心,或者亞馬遜 S3 中。
RDB 可以最大化 Redis 的效能:父程序在儲存 RDB 檔案時唯一要做的就是 fork 出一個子程序,然後這個子程序就會處理接下來的所有儲存工作,父程序無須執行任何磁碟 I/O 操作。RDB 在恢復大資料集時的速度比 AOF 的恢復速度要快。
RDB 的缺點:
如果你需要儘量避免在伺服器故障時丟失資料,那麼 RDB 不適合你。 雖然 Redis 允許你設定不同的儲存點(save point)來控制儲存 RDB 檔案的頻率, 但是, 因為RDB 檔案需要儲存整個資料集的狀態, 所以它並不是一個輕鬆的操作。
因此你可能會至少 5 分鐘才儲存一次 RDB 檔案。 在這種情況下, 一旦發生故障停機, 你就可能會丟失好幾分鐘的資料。每次儲存 RDB 的時候,Redis 都要 fork() 出一個子程序,並由子程序來進行實際的持久化工作。
在資料集比較龐大時, fork() 可能會非常耗時,造成伺服器在某某毫秒內停止處理客戶端; 如果資料集非常巨大,並且 CPU 時間非常緊張的話,那麼這種停止時間甚至可能會長達整整一秒。
雖然 AOF 重寫也需要進行 fork() ,但無論 AOF 重寫的執行間隔有多長,資料的耐久性都不會有任何損失。
二:AOF(Append-only file)
filesnapshotting方法在redis異常死掉時,最近的資料會丟失(丟失資料的多少視你save策略的配置),所以這是它最大的缺點,當業務量很大時,丟失的資料是很多的。Append-only方法可以做到全部資料不丟失,
但redis的效能就要差些。AOF就可以做到全程持久化,只需要在配置檔案中開啟(預設是no),appendonly yes開啟AOF之後,redis每執行一個修改資料的命令,都會把它新增到aof檔案中,當redis重啟時,將會讀取AOF檔案進行“重放”以恢復到redis關閉前的最後時刻。
AOF檔案重新整理的方式,有三種,參考配置引數appendfsync :
appendfsync always每提交一個修改命令都呼叫fsync重新整理到AOF檔案,非常非常慢,但也非常安全;
appendfsync everysec每秒鐘都呼叫fsync重新整理到AOF檔案,很快,但可能會丟失一秒以內的資料;
appendfsync no依靠OS進行重新整理,redis不主動重新整理AOF,這樣最快,但安全性就差。預設並推薦每秒重新整理,這樣在速度和安全上都做到了兼顧。
AOF 的優點:
使用 AOF 持久化會讓 Redis 變得非常耐久(much more durable):你可以設定不同的 fsync 策略,比如無 fsync ,每秒鐘一次 fsync ,或者每次執行寫入命令時 fsync 。
AOF 的預設策略為每秒鐘 fsync 一次,在這種配置下,Redis 仍然可以保持良好的效能,並且就算髮生故障停機,也最多隻會丟失一秒鐘的資料( fsync 會在後臺執行緒執行,所以主執行緒可以繼續努力地處理命令請求)。
AOF 檔案是一個只進行追加操作的日誌檔案(append only log), 因此對 AOF 檔案的寫入不需要進行 seek , 即使日誌因為某些原因而包含了未寫入完整的命令(比如寫入時磁碟已滿,寫入中途停機,等等), redis-check-aof 工具也可以輕易地修復這種問題。
Redis 可以在 AOF 檔案體積變得過大時,自動地在後臺對 AOF 進行重寫: 重寫後的新 AOF 檔案包含了恢復當前資料集所需的最小命令集合。
整個重寫操作是絕對安全的,因為 Redis 在建立新 AOF 檔案的過程中,會繼續將命令追加到現有的 AOF 檔案裡面,即使重寫過程中發生停機,現有的 AOF 檔案也不會丟失。 而一旦新 AOF 檔案建立完畢,Redis 就會從舊 AOF 檔案切換到新 AOF 檔案,並開始對新 AOF 檔案進行追加操作。
AOF 檔案有序地儲存了對資料庫執行的所有寫入操作, 這些寫入操作以 Redis 協議的格式儲存, 因此 AOF 檔案的內容非常容易被人讀懂, 對檔案進行分析(parse)也很輕鬆。
匯出(export) AOF 檔案也非常簡單: 舉個例子, 如果你不小心執行了 FLUSHALL 命令, 但只要 AOF 檔案未被重寫, 那麼只要停止伺服器, 移除 AOF 檔案末尾的 FLUSHALL 命令, 並重啟 Redis , 就可以將資料集恢復到 FLUSHALL 執行之前的狀態。
AOF 的缺點:
對於相同的資料集來說,AOF 檔案的體積通常要大於 RDB 檔案的體積。
根據所使用的 fsync 策略,AOF 的速度可能會慢於 RDB 。 在一般情況下, 每秒 fsync 的效能依然非常高, 而關閉 fsync 可以讓 AOF 的速度和 RDB 一樣快, 即使在高負荷之下也是如此。
不過在處理巨大的寫入載入時,RDB 可以提供更有保證的最大延遲時間(latency)。AOF 在過去曾經發生過這樣的 bug : 因為個別命令的原因,導致 AOF 檔案在重新載入時,無法將資料集恢復成儲存時的原樣。
(舉個例子,阻塞命令 BRPOPLPUSH 就曾經引起過這樣的 bug 。) 測試套件裡為這種情況添加了測試: 它們會自動生成隨機的、複雜的資料集, 並通過重新載入這些資料來確保一切正常。
雖然這種 bug 在 AOF 檔案中並不常見, 但是對比來說, RDB 幾乎是不可能出現這種 bug 的。
可能由於系統原因導致了AOF損壞,redis無法再載入這個AOF,可以按照下面步驟來修復:首先做一個AOF檔案的備份,複製到其他地方;修復原始AOF檔案,執行:$ redis-check-aof –fix ;可以通過diff –u命令來檢視修復前後文件不一致的地方;重啟redis服務。
LOG Rewrite的工作原理:同樣用到了copy-on-write:首先redis會fork一個子程序;子程序將最新的AOF寫入一個臨時檔案;父程序增量的把記憶體中的最新執行的修改寫入(這時仍寫入舊的AOF,rewrite如果失敗也是安全的);當子程序完成rewrite臨時檔案後,父程序會收到一個訊號,並把之前記憶體中增量的修改寫入臨時檔案末尾;這時redis將舊AOF檔案重新命名,臨時檔案重新命名,開始向新的AOF中寫入。
最後,為以防萬一(機器壞掉或磁碟壞掉),記得定期把使用 filesnapshotting 或 Append-only 生成的*rdb *.aof檔案備份到遠端機器上。我是用crontab每半小時SCP一次。我沒有使用redis的主從功能 ,因為半小時備份一次應該是可以了,而且我覺得有如果做主從有點浪費機器。這個最終還是看應用來定了。
《Redis官方文件》使用Redis作為LRU快取
如果你使用redis作為快取,當新增新資料時,若有記憶體大小等限制,系統預設會根據一定的規則自動清理舊資料。這種處理方式在開發社群中眾所周知,因為它也是非常流行的快取系統 memcached 的預設處理方式。
LRU(LRU全稱是Least Recently Used,即最近最久未使用)實際上只是Redis支援的記憶體回收策略中的一種,
Redis的 maxmemory 配置選項,該配置選項用來限制 Redis 的記憶體使用大小,同時深入研究 LRU(確切的說是近似LRU演算法) 演算法在 Redis 中的應用。
最大記憶體配置選項
maxmemory 配置選項使用來配置 Redis 的儲存資料所能使用的最大記憶體限制。可以通過在內建檔案redis.conf中配置,也可在Redis執行時通過命令CONFIG SET來配置。例如,我們要配置記憶體上限是100M的Redis快取,那麼我們可以在 redis.conf 配置如下:
maxmemory 100mb
設定 maxmemory 為 0 表示沒有記憶體限制。在 64-bit 系統中,預設是 0 無限制,但是在 32-bit 系統中預設是 3GB。
當儲存資料達到限制時,Redis 會根據情形選擇不同策略,或者返回errors(這樣會導致浪費更多的記憶體),或者清除一些舊資料回收記憶體來新增新資料
回收策略
當記憶體達到限制時,Redis 具體的回收策略是通過 maxmemory-policy 配置項配置的。
以下的策略都是可用的:
- noenviction:不清除資料,只是返回錯誤,這樣會導致浪費掉更多的記憶體,對大多數寫命令(DEL 命令和其他的少數命令例外)
- allkeys-lru:從所有的資料集(server.db[i].dict)中挑選最近最少使用的資料淘汰,以供新資料使用
- volatile-lru:從已設定過期時間的資料集(server.db[i].expires)中挑選最近最少使用的資料淘汰,以供新資料使用
- allkeys-random:從所有資料集(server.db[i].dict)中任意選擇資料淘汰,以供新資料使用
- volatile-random:從已設定過期時間的資料集(server.db[i].expires)中任意選擇資料淘汰,以供新資料使用
- volatile-ttl:從已設定過期時間的資料集(server.db[i].expires)中挑選將要過期的資料淘汰,以供新資料使用
當 cache 中沒有符合清除條件的 key 時,回收策略 volatile-lru, volatile-random 和volatile-ttl 將會和 策略 noeviction 一樣返回錯誤。選擇正確的回收策略是很重要的,取決於你的應用程式的訪問模式。但是,你可以在程式執行時重新配置策略,使用 INFO 輸出來監控快取命中和錯過的次數,以調優你的設定。
普適經驗規則:
- 如果期望使用者請求呈現冪律分佈(power-law distribution),也就是,期望一部分子集元素被訪問得遠比其他元素多時,可以使用allkeys-lru策略。在你不確定時這是一個好的選擇。
- 如果期望是迴圈週期的訪問,所有的鍵被連續掃描,或者期望請求符合平均分佈(每個元素以相同的概率被訪問),可以使用allkeys-random策略。
- 如果你期望能讓 Redis 通過使用你建立快取物件的時候設定的TTL值,確定哪些物件應該是較好的清除候選項,可以使用volatile-ttl策略。
當你想使用單個Redis例項來實現快取和持久化一些鍵,allkeys-lru和volatile-random策略會很有用。但是,通常最好是執行兩個Redis例項來解決這個問題。
另外值得注意的是,為鍵設定過期時間需要消耗記憶體,所以使用像allkeys-lru這樣的策略會更高效,因為在記憶體壓力下沒有必要為鍵的回收設定過期時間。
回收過程
理解回收過程是運作流程非常的重要,回收過程如下:
- 一個客戶端執行一個新命令,添加了新資料。
- Redis檢查記憶體使用情況,如果大於maxmemory限制,根據策略來回收鍵。
- 一個新的命令被執行,如此等等。
我們新增資料時通過檢查,然後回收鍵以返回到限制以下,來連續不斷的穿越記憶體限制的邊界。
如果一個命令導致大量的記憶體被佔用(比如一個很大的集合儲存到一個新的鍵),那麼記憶體限制很快就會被這個明顯的記憶體量所超越。
近似LRU演算法
Redis的LRU演算法不是一個嚴格的LRU實現。這意味著Redis不能選擇最佳候選鍵來回收,也就是最久未被訪問的那些鍵。相反,Redis 會嘗試執行一個近似的LRU演算法,通過取樣一小部分鍵,然後在取樣鍵中回收最適合(擁有最久訪問時間)的那個。
然而,從Redis3.0開始,演算法被改進為維護一個回收候選鍵池。這改善了演算法的效能,使得更接近於真實的LRU演算法的行為。Redis的LRU演算法有一點很重要,你可以調整演算法的精度,通過改變每次回收時檢查的取樣數量。
這個引數可以通過如下配置指令:
maxmemory-samples 5
Redis沒有使用真實的LRU實現的原因,是因為這會消耗更多的記憶體。然而,近似值對使用Redis的應用來說基本上也是等價的。下面的圖形對比,為Redis使用的LRU近似值和真實LRU之間的比較。
用於測試生成了上面影象的Redis服務被填充了指定數量的鍵。鍵被從頭訪問到尾,所以第一個鍵是LRU演算法的最佳候選回收鍵。然後,再新新增50%的鍵,強制一般的舊鍵被回收。
你可以從圖中看到三種不同的原點,形成三個不同的帶。
- 淺灰色帶是被回收的物件
- 灰色帶是沒有被回收的物件
- 綠色帶是被新增的物件
在理論的LRU實現中,我們期望看到的是,在舊鍵中第一半會過期。而Redis的LRU演算法則只是概率性的過期這些舊鍵。 你可以看到,同樣使用5個取樣點,Redis 3.0表現得比Redis 2.8要好,Redis 2.8中最近被訪問的物件之間的物件仍然被保留。在Redis 3.0中使用10為取樣大小,近似值已經非常接近理論效能。
注意,LRU只是一個預測模型用來指定鍵在未來如何被訪問。另外,如果你的資料訪問模式非常接近冪律,大多數的訪問都將集中在一個集合中,LRU近似演算法將能處理得很好。
在模擬實驗的過程中,我們發現使用冪律訪問模式,真實的LRU演算法和Redis的近似演算法之間的差異非常小,或者根本就沒有。然而,你可以提高取樣大小到10,這會消耗額外的CPU,來更加近似於真實的LRU演算法,看看這會不會使你的快取錯失率有差異。
使用CONFIG SET maxmemory-samples <count>命令在生產環境上試驗各種不同的取樣大小值是很簡單的。