數(shù)據(jù)持久化通俗講就是把數(shù)據(jù)保存到磁盤上,保證不會(huì)因?yàn)閿嚯姷纫蛩貋G失數(shù)據(jù)。
redis需要經(jīng)常將內(nèi)存中的數(shù)據(jù)同步到磁盤來保證持久化。redis支持兩種持久化方式,一種是 Snapshotting(快照)也是默認(rèn)方式,另一種是Append-only file(縮寫aof)的方式。先介紹下這兩種dump方式再講講自己遇到的一些現(xiàn)象和想法,前面的內(nèi)容是從網(wǎng)上整理出來的。
Snapshotting
快照是默認(rèn)的持久化方式。這種方式是就是將內(nèi)存中數(shù)據(jù)以快照的方式寫入到二進(jìn)制文件中,默認(rèn)的文件名為dump.rdb。可以通過配置設(shè)置自動(dòng)做快照持久 化的方式。我們可以配置redis在n秒內(nèi)如果超過m個(gè)key被修改就自動(dòng)做快照,下面是默認(rèn)的快照保存配置
save 900 1 #900秒內(nèi)如果超過1個(gè)key被修改,則發(fā)起快照保存
save 300 10 #300秒內(nèi)容如超過10個(gè)key被修改,則發(fā)起快照保存
save 60 10000
下面介紹詳細(xì)的快照保存過程
1.redis調(diào)用fork,現(xiàn)在有了子進(jìn)程和父進(jìn)程。
2. 父進(jìn)程繼續(xù)處理client請求,子進(jìn)程負(fù)責(zé)將內(nèi)存內(nèi)容寫入到臨時(shí)文件。由于os的寫時(shí)復(fù)制機(jī)制(copy on write)父子進(jìn)程會(huì)共享相同的物理頁面,當(dāng)父進(jìn)程處理寫請求時(shí)os會(huì)為父進(jìn)程要修改的頁面創(chuàng)建副本,而不是寫共享的頁面。所以子進(jìn)程的地址空間內(nèi)的數(shù) 據(jù)是fork時(shí)刻整個(gè)數(shù)據(jù)庫的一個(gè)快照。
3.當(dāng)子進(jìn)程將快照寫入臨時(shí)文件完畢后,用臨時(shí)文件替換原來的快照文件,然后子進(jìn)程退出。
client 也可以使用save或者bgsave命令通知redis做一次快照持久化。save操作是在主線程中保存快照的,由于redis是用一個(gè)主線程來處理所有 client的請求,這種方式會(huì)阻塞所有client請求。所以不推薦使用。另一點(diǎn)需要注意的是,每次快照持久化都是將內(nèi)存數(shù)據(jù)完整寫入到磁盤一次,并不 是增量的只同步臟數(shù)據(jù)。如果數(shù)據(jù)量大的話,而且寫操作比較多,必然會(huì)引起大量的磁盤io操作,可能會(huì)嚴(yán)重影響性能。
另外由于快照方式是在一定間隔時(shí)間做一次的,所以如果redis意外down掉的話,就會(huì)丟失最后一次快照后的所有修改。如果應(yīng)用要求不能丟失任何修改的話,可以采用aof持久化方式。下面介紹
Append-only file
aof 比快照方式有更好的持久化性,是由于在使用aof持久化方式時(shí),redis會(huì)將每一個(gè)收到的寫命令都通過write函數(shù)追加到文件中(默認(rèn)是 appendonly.aof)。當(dāng)redis重啟時(shí)會(huì)通過重新執(zhí)行文件中保存的寫命令來在內(nèi)存中重建整個(gè)數(shù)據(jù)庫的內(nèi)容。當(dāng)然由于os會(huì)在內(nèi)核中緩存 write做的修改,所以可能不是立即寫到磁盤上。這樣aof方式的持久化也還是有可能會(huì)丟失部分修改。不過我們可以通過配置文件告訴redis我們想要 通過fsync函數(shù)強(qiáng)制os寫入到磁盤的時(shí)機(jī)。有三種方式如下(默認(rèn)是:每秒fsync一次)
appendonly yes //啟用aof持久化方式
# appendfsync always //每次收到寫命令就立即強(qiáng)制寫入磁盤,最慢的,但是保證完全的持久化,不推薦使用
appendfsync everysec //每秒鐘強(qiáng)制寫入磁盤一次,在性能和持久化方面做了很好的折中,推薦
# appendfsync no //完全依賴os,性能最好,持久化沒保證
aof 的方式也同時(shí)帶來了另一個(gè)問題。持久化文件會(huì)變的越來越大。例如我們調(diào)用incr test命令100次,文件中必須保存全部的100條命令,其實(shí)有99條都是多余的。因?yàn)橐謴?fù)數(shù)據(jù)庫的狀態(tài)其實(shí)文件中保存一條set test 100就夠了。為了壓縮aof的持久化文件。redis提供了bgrewriteaof命令。收到此命令redis將使用與快照類似的方式將內(nèi)存中的數(shù)據(jù) 以命令的方式保存到臨時(shí)文件中,最后替換原來的文件。具體過程如下
1. redis調(diào)用fork ,現(xiàn)在有父子兩個(gè)進(jìn)程
2. 子進(jìn)程根據(jù)內(nèi)存中的數(shù)據(jù)庫快照,往臨時(shí)文件中寫入重建數(shù)據(jù)庫狀態(tài)的命令
3.父進(jìn)程繼續(xù)處理client請求,除了把寫命令寫入到原來的aof文件中。同時(shí)把收到的寫命令緩存起來。這樣就能保證如果子進(jìn)程重寫失敗的話并不會(huì)出問題。
4.當(dāng)子進(jìn)程把快照內(nèi)容寫入已命令方式寫到臨時(shí)文件中后,子進(jìn)程發(fā)信號(hào)通知父進(jìn)程。然后父進(jìn)程把緩存的寫命令也寫入到臨時(shí)文件。
5.現(xiàn)在父進(jìn)程可以使用臨時(shí)文件替換老的aof文件,并重命名,后面收到的寫命令也開始往新的aof文件中追加。
需要注意到是重寫aof文件的操作,并沒有讀取舊的aof文件,而是將整個(gè)內(nèi)存中的數(shù)據(jù)庫內(nèi)容用命令的方式重寫了一個(gè)新的aof文件,這點(diǎn)和快照有點(diǎn)類似。
運(yùn)維上的想法
其實(shí)快照和aof一樣,都使用了Copy-on-write技術(shù)。多次試驗(yàn)發(fā)現(xiàn)每次做數(shù)據(jù)dump的時(shí)候,內(nèi)存都會(huì)擴(kuò)大一倍(關(guān)于這個(gè)問題可以參考我去年寫的 redis的內(nèi)存陷阱 ,很多人用redis做為緩存,數(shù)據(jù)量小,dump耗時(shí)非常短暫,所以不太容易發(fā)現(xiàn)),這個(gè)時(shí)候會(huì)有三種情況:
一:物理內(nèi)存足以滿足,這個(gè)時(shí)候dump非常快,性能最好
二:物理內(nèi)存+虛擬內(nèi)存可以滿足,這個(gè)時(shí)候dump速度會(huì)比較慢,磁盤swap繁忙,服務(wù)性能也會(huì)下降。所幸的是經(jīng)過一段比較長的時(shí)候數(shù)據(jù)dump完成了,然后內(nèi)存恢復(fù)正常。這個(gè)情況系統(tǒng)穩(wěn)定性差。
三: 物理內(nèi)存+虛擬內(nèi)存不能滿足,這個(gè)時(shí)候dump一直死著,時(shí)間久了機(jī)器掛掉。這個(gè)情況就是災(zāi)難!
如果數(shù)據(jù)要做持久化又想保證穩(wěn)定性,建議留空一半的物理內(nèi)存。如果覺得無法接受還是有辦法,下面講:
快照和aof雖然都使用Copy-on-write,但有個(gè)不同點(diǎn),快照你無法預(yù)測redis什么時(shí)候做dump,aof可以通過bgrewriteaof命令控制dump的時(shí)機(jī)。
根據(jù)這點(diǎn)我可以在一個(gè)服務(wù)器上開啟多個(gè)redis節(jié)點(diǎn)(利用多CPU),使用aof的持久化方式。
例如在24G內(nèi)存的服務(wù)器上開啟3個(gè)節(jié)點(diǎn),每天用bgrewriteaof定期重新整理數(shù)據(jù),每個(gè)節(jié)點(diǎn)dump的時(shí)間都不一樣,這樣理論上每個(gè)節(jié)點(diǎn)可以消耗6G內(nèi)存,一共使用18G內(nèi)存,另外6G內(nèi)存在單個(gè)節(jié)點(diǎn)dump時(shí)用到,內(nèi)存一下多利用了6G! 當(dāng)然節(jié)點(diǎn)開的越多內(nèi)存的利用率也越高。如果帶寬不是問題,節(jié)點(diǎn)數(shù)建議 = CPU數(shù)。
我的應(yīng)用里為了保證高性能,數(shù)據(jù)沒有做dump,也沒有用aof。因?yàn)椴蛔鰀ump發(fā)生的故障遠(yuǎn)遠(yuǎn)低于做dump的時(shí)候,即使數(shù)據(jù)丟失了,自動(dòng)修復(fù)腳本可以馬上數(shù)據(jù)恢復(fù)。畢竟對海量數(shù)據(jù)redis只能做數(shù)據(jù)分片,那么落到每個(gè)節(jié)點(diǎn)上的數(shù)據(jù)量也不會(huì)很多。
redis的虛擬內(nèi)存建議也不要用,用redis本來就是為了達(dá)到變態(tài)的性能,虛擬內(nèi)存、aof看起來都有些雞肋。
現(xiàn)在還離不開redis,因?yàn)樗膍get是現(xiàn)在所有db里性能最好的,以前也考慮過用tokyocabinet hash方式做mget,性能不給力。直接用redis,基本上單個(gè)redis節(jié)點(diǎn)mget可以達(dá)到10W/s
糾錯(cuò)
之前說過redis做數(shù)據(jù)dump的時(shí)候內(nèi)容會(huì)擴(kuò)大一倍,后來我又做了些測試,發(fā)現(xiàn)有些地方說的不對。
top命令并不是反映真實(shí)的內(nèi)存占用情況,在top里盡管fork出來的子進(jìn)程占了和父進(jìn)程一樣的內(nèi)存,但是當(dāng)做dump的時(shí)候沒有寫操作,實(shí)際使用的是同一份內(nèi)存的數(shù)據(jù)。當(dāng)有寫操作的時(shí)候內(nèi)存才會(huì)真實(shí)的擴(kuò)大(具體是不是真實(shí)的擴(kuò)大一倍不確定,可能數(shù)據(jù)是按照頁分片的),這才是真正的Copy-on-write。
基于這點(diǎn)在做數(shù)據(jù)持久化會(huì)更加靈活。
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
