redis 是一個支援持久化的記憶體資料庫,也就是說redis 需要經常将記憶體中的資料同步到磁盤
來保證持久化。redis 支援兩種持久化方式,一種是Snapshotting(快照)也是預設方式,另
一種是Append-only file(縮寫aof)的方式。下面分别介紹:
一、snapshotting 方式
快照是預設的持久化方式。這種方式是就是将記憶體中資料以快照的方式寫入到二進制檔案中,
預設的檔案名為dump.rdb。可以通過配置設定自動做快照持久化的方式。
我們可以配置redis在n 秒内如果超過m 個key 被修改就自動做快照,
下面是預設的快照儲存配置
1
2
3
<code>save 900 1 </code><code>#900 秒内如果超過1 個key 被修改,則發起快照儲存</code>
<code>save 300 10 </code><code>#300 秒内容如超過10 個key 被修改,則發起快照儲存</code>
<code>save 60 10000</code>
下面介紹詳細的快照儲存過程:
1、redis 調用fork,現在有了子程序和父程序。
2.、父程序繼續處理client 請求,子程序負責将記憶體内容寫入到臨時檔案。由于os 的實時複
制機制(copy on write)父子程序會共享相同的實體頁面,當父程序處理寫請求時os 會為父
程序要修改的頁面建立副本,而不是寫共享的頁面。是以子程序位址空間内的資料是fork
時刻整個資料庫的一個快照。
3、當子程序将快照寫入臨時檔案完畢後,用臨時檔案替換原來的快照檔案,然後子程序退出。
client 也可以使用save 或者bgsave 指令通知redis 做一次快照持久化。save 操作是在主線程
中儲存快照的,由于redis 是用一個主線程來處理所有client 的請求,這種方式會阻塞所有
client 請求。是以不推薦使用。另一點需要注意的是,每次快照持久化都是将記憶體資料完整
寫入到磁盤一次,并不是增量的隻同步變更資料。如果資料量大的話,而且寫操作比較多,
必然會引起大量的磁盤io 操作,可能會嚴重影響性能。
下面将示範各種場景的資料庫持久化情況
首先在redis.conf配置檔案中配置log日志
<a href="http://blog.51cto.com/attachment/201312/204253940.png" target="_blank"></a>
然後我們進行如下操作:
<a href="http://blog.51cto.com/attachment/201312/203032247.png" target="_blank"></a>
檢視日志檔案
<a href="http://blog.51cto.com/attachment/201312/204220110.png" target="_blank"></a>
從日志可以看出,資料庫做了一個存盤的操作,将記憶體的資料寫入磁盤了。正常的話,磁盤
上會産生一個dump 檔案,用于儲存資料庫快照,我們來驗證一下:
硬碟上已經産生了一個資料庫快照了。這時侯我們再将redis 啟動,看鍵值還是否真的持久
化到硬碟了。
<a href="http://blog.51cto.com/attachment/201312/204449573.png" target="_blank"></a>
二、aof方式
另外由于快照方式是在一定間隔時間做一次的,是以如果redis 意外down 掉的話,就會丢
失最後一次快照後的所有修改。如果應用要求不能丢失任何修改的話,可以采用aof 持久化
方式。
下面介紹Append-only file:
aof 比快照方式有更好的持久化性,是由于在使用aof 持久化方式時,redis 會将每一個收到
的寫指令都通過write 函數追加到檔案中(預設是appendonly.aof)。當redis 重新開機時會通過重
新執行檔案中儲存的寫指令來在記憶體中重建整個資料庫的内容。
當然由于os 會在核心中緩存 write 做的修改,是以可能不是立即寫到磁盤上。這樣aof 方式的持久化也還是有可能會丢失部分修改。
不過我們可以通過配置檔案告訴redis 我們想要通過fsync 函數強制os 寫入
到磁盤的時機。有三種方式如下(預設是:每秒fsync 一次)
4
<code>appendonly </code><code>yes</code> <code>#啟用aof 持久化方式</code>
<code>appendfsync always </code><code>#收到寫指令就立即寫入磁盤,最慢,但是保證完全的持久化</code>
<code>appendfsync everysec </code><code>#每秒鐘寫入磁盤一次,在性能和持久化方面做了很好的折中</code>
<code>appendfsync no </code><code>#完全依賴os,性能最好,持久化沒保證</code>
接下來我們以執行個體說明用法:
修改配置檔案:
<a href="http://blog.51cto.com/attachment/201312/205352695.png" target="_blank"></a>
進行一下操作:
<a href="http://blog.51cto.com/attachment/201312/205201138.png" target="_blank"></a>
我們先設定2 個鍵值對,然後我們看一下系統中有沒有産生appendonly.aof 檔案
<a href="http://blog.51cto.com/attachment/201312/205455156.png" target="_blank"></a>
結果證明産生了,接着我們将redis 再次啟動後來看一下資料是否還在
<a href="http://blog.51cto.com/attachment/201312/205537863.png" target="_blank"></a>
資料還存在系統中,說明系統是在啟動時執行了一下從磁盤到記憶體的load 資料的過程。
aof 的方式也同時帶來了另一個問題。持久化檔案會變的越來越大。例如我們調用incr test
指令100 次,檔案中必須儲存全部的100 條指令,其實有99 條都是多餘的。因為要恢複數
據庫的狀态其實檔案中儲存一條set test 100 就夠了。為了壓縮aof 的持久化檔案。redis 提
供了bgrewriteaof 指令。收到此指令redis 将使用與快照類似的方式将記憶體中的資料以指令
的方式儲存到臨時檔案中,最後替換原來的檔案。具體過程如下
1、redis 調用fork ,現在有父子兩個程序
2、子程序根據記憶體中的資料庫快照,往臨時檔案中寫入重建資料庫狀态的指令
3、父程序繼續處理client 請求,除了把寫指令寫入到原來的aof 檔案中。同時把收到的寫命
令緩存起來。這樣就能保證如果子程序重寫失敗的話并不會出問題。
4、當子程序把快照内容寫入已指令方式寫到臨時檔案中後,子程序發信号通知父程序。然
後父程序把緩存的寫指令也寫入到臨時檔案。
5、現在父程序可以使用臨時檔案替換老的aof 檔案,并重命名,後面收到的寫指令也開始
往新的aof 檔案中追加。
需要注意到是重寫aof 檔案的操作,并沒有讀取舊的aof 檔案,而是将整個記憶體中的資料庫
内容用指令的方式重寫了一個新的aof 檔案,這點和快照有點類似。接來我們看一下實際的例
子:
我們先調用5 次incr age 指令:
<a href="http://blog.51cto.com/attachment/201312/210429407.png" target="_blank"></a>
接下來我們看一下日志檔案的大小
<a href="http://blog.51cto.com/attachment/201312/210537573.png" target="_blank"></a>
大小為533 個位元組,接下來我們調用一下bgrewriteaof 指令将記憶體中的資料重新刷到磁盤的
日志檔案中
<a href="http://blog.51cto.com/attachment/201312/210706678.png" target="_blank"></a>
再看一下磁盤上的日志檔案大小
<a href="http://blog.51cto.com/attachment/201312/210751278.png" target="_blank"></a>
日志檔案大小變為84 個位元組了,說明原來日志中的重複記錄已被重新整理掉了。
本文轉自shayang8851CTO部落格,原文連結:http://blog.51cto.com/janephp/1340039,如需轉載請自行聯系原作者