Redis是一種面向「key-value」類型數據的分佈式NoSQL數據庫系統,具備高性能、持久存儲、適應高併發應用場景等優點。它雖然起步較晚,但發展卻十分迅速。
近日,Redis的做者在博客中寫到,他看到的全部針對Redis的討論中,對Redis持久化的誤解是最大的,因而他寫了一篇長文來對Redis的持久化進行了系統性的論述。javascript
文章主要包含三個方面:Redis持久化是如何工做的、這一性能是否可靠以及和其它類型的數據庫比較。如下爲文章內容:
1、Redis持久化是如何工做的?
什麼是持久化?簡單來說就是將數據放到斷電後數據不會丟失的設備中,也就是咱們一般理解的硬盤上。html
首先咱們來看一下數據庫在進行寫操做時到底作了哪些事,主要有下面五個過程: java
故障分析
寫操做大體有上面5個流程,下面咱們結合上面的5個流程看一下各類級別的故障: nginx
經過上面5步的瞭解,可能咱們會但願搞清下面一些問題: redis
對於第一個問題,一般數據庫層面會進行全面控制。數據庫
而對第二個問題,操做系統有其默認的策略,可是咱們也能夠經過POSIX API提供的fsync系列命令強制操做系統將數據從內核區寫到磁盤控制器上。緩存
對於第三個問題,好像數據庫已經沒法觸及,但實際上,大多數狀況下磁盤緩存是被設置關閉的,或者是隻開啓爲讀緩存,也就是說寫操做不會進行緩存,直接寫到磁盤。安全
建議的作法是僅僅當你的磁盤設備有備用電池時纔開啓寫緩存。
數據損壞
所謂數據損壞,就是數據沒法恢復,上面咱們講的都是如何保證數據是確實寫到磁盤上去,可是寫到磁盤上可能並不意味着數據不會損壞。好比咱們可能一次寫請求會進行兩次不一樣的寫操做,當意外發生時,可能會致使一次寫操做安全完成,可是另外一次尚未進行。若是數據庫的數據文件結構組織不合理,可能就會致使數據徹底不能恢復的情況出現。
這裏一般也有三種策略來組織數據,以防止數據文件損壞到沒法恢復的狀況:
服務器
RDB持久化是指在指定的時間間隔內將內存中的數據集快照寫入磁盤。併發
也是默認的持久化方式,這種方式是就是將內存中數據以快照的方式寫入到二進制文件中,默認的文件名爲dump.rdb。
能夠經過配置設置自動作快照持久化的方式。咱們能夠配置redis在n秒內若是超過m個key被修改就自動作快照,下面是默認的快照保存配置
save 900 1 #900秒內若是超過1個key被修改,則發起快照保存 save 300 10 #300秒內容如超過10個key被修改,則發起快照保存 save 60 10000
client 也可使用save或者bgsave命令通知redis作一次快照持久化。save操做是在主線程中保存快照的,因爲redis是用一個主線程來處理全部 client的請求,這種方式會阻塞全部client請求。因此不推薦使用。
另外一點須要注意的是,每次快照持久化都是將內存數據完整寫入到磁盤一次,並不 是增量的只同步髒數據。若是數據量大的話,並且寫操做比較多,必然會引發大量的磁盤io操做,可能會嚴重影響性能。
redis會將每個收到的寫命令都經過write函數追加到文件中(默認是 appendonly.aof)。
當redis重啓時會經過從新執行文件中保存的寫命令來在內存中重建整個數據庫的內容。固然因爲os會在內核中緩存 write作的修改,因此可能不是當即寫到磁盤上。這樣aof方式的持久化也仍是有可能會丟失部分修改。不過咱們能夠經過配置文件告訴redis咱們想要 經過fsync函數強制os寫入到磁盤的時機。有三種方式以下(默認是:每秒fsync一次)
appendonly yes //啓用aof持久化方式 # appendfsync always //每次收到寫命令就當即強制寫入磁盤,最慢的,可是保證徹底的持久化,不推薦使用 appendfsync everysec //每秒鐘強制寫入磁盤一次,在性能和持久化方面作了很好的折中,推薦 # appendfsync no //徹底依賴os,性能最好,持久化沒保證
aof 的方式也同時帶來了另外一個問題。持久化文件會變的愈來愈大。例如咱們調用incr test命令100次,文件中必須保存所有的100條命令,其實有99條都是多餘的。由於要恢復數據庫的狀態其實文件中保存一條set test 100就夠了。
爲了壓縮aof的持久化文件。redis提供了bgrewriteaof命令。收到此命令redis將使用與快照相似的方式將內存中的數據 以命令的方式保存到臨時文件中,最後替換原來的文件。具體過程以下
須要注意到是重寫aof文件的操做,並無讀取舊的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 文件的體積一般要大於 RDB 文件的體積。
根據所使用的 fsync 策略,AOF 的速度可能會慢於 RDB 。 在通常狀況下, 每秒 fsync 的性能依然很是高, 而關閉 fsync 可讓 AOF 的速度和 RDB 同樣快, 即便在高負荷之下也是如此。 不過在處理巨大的寫入載入時,RDB 能夠提供更有保證的最大延遲時間(latency)。
AOF 在過去曾經發生過這樣的 bug : 由於個別命令的緣由,致使 AOF 文件在從新載入時,沒法將數據集恢復成保存時的原樣。 (舉個例子,阻塞命令 BRPOPLPUSH 就曾經引發過這樣的 bug 。) 測試套件裏爲這種狀況添加了測試: 它們會自動生成隨機的、複雜的數據集, 並經過從新載入這些數據來確保一切正常。 雖然這種 bug 在 AOF 文件中並不常見, 可是對比來講, RDB 幾乎是不可能出現這種 bug 的。
通常來講, 若是想達到足以媲美 PostgreSQL 的數據安全性, 你應該同時使用兩種持久化功能。
若是你很是關心你的數據, 但仍然能夠承受數分鐘之內的數據丟失, 那麼你能夠只使用 RDB 持久化。
其他狀況我我的喜愛選擇AOF
三
1. Snapshotting:
缺省狀況下,Redis會將數據集的快照dump到dump.rdb文件中。此外,咱們也能夠經過配置文件來修改Redis服務器dump快照的頻率,在打開6379.conf文件以後,咱們搜索save,能夠看到下面的配置信息:
save 900 1 #在900秒(15分鐘)以後,若是至少有1個key發生變化,則dump內存快照。
save 300 10 #在300秒(5分鐘)以後,若是至少有10個key發生變化,則dump內存快照。
save 60 10000 #在60秒(1分鐘)以後,若是至少有10000個key發生變化,則dump內存快照。
2. Dump快照的機制:
1). Redis先fork子進程。
2). 子進程將快照數據寫入到臨時RDB文件中。
3). 當子進程完成數據寫入操做後,再用臨時文件替換老的文件。
5.4.3. AOF文件:
上面已經屢次講過,RDB的快照定時dump機制沒法保證很好的數據持久性。若是咱們的應用確實很是關注此點,咱們能夠考慮使用Redis中的AOF機制。對於Redis服務器而言,其缺省的機制是RDB,若是須要使用AOF,則須要修改配置文件中的如下條目:
將appendonly no改成appendonly yes
從如今起,Redis在每一次接收到數據修改的命令以後,都會將其追加到AOF文件中。在Redis下一次從新啓動時,須要加載AOF文件中的信息來構建最新的數據到內存中。
5.4.5. AOF的配置:
在Redis的配置文件中存在三種同步方式,它們分別是:
appendfsync always #每次有數據修改發生時都會寫入AOF文件。
appendfsync everysec #每秒鐘同步一次,該策略爲AOF的缺省策略。
appendfsync no #從不一樣步。高效可是數據不會被持久化。
5.4.6. 如何修復壞損的AOF文件:
1). 將現有已經壞損的AOF文件額外拷貝出來一份。
2). 執行"redis-check-aof --fix <filename>"命令來修復壞損的AOF文件。
3). 用修復後的AOF文件從新啓動Redis服務器。
5.4.7. Redis的數據備份:
在Redis中咱們能夠經過copy的方式在線備份正在運行的Redis數據文件。這是由於RDB文件一旦被生成以後就不會再被修改。Redis每次都是將最新的數據dump到一個臨時文件中,以後在利用rename函數原子性的將臨時文件更名爲原有的數據文件名。所以咱們能夠說,在任意時刻copy數據文件都是安全的和一致的。鑑於此,咱們就能夠經過建立cron job的方式定時備份Redis的數據文件,並將備份文件copy到安全的磁盤介質中。
5.五、當即寫入
//當即保存,同步保存 public static void syncSave() throws Exception{ Jedis jedis=new Jedis("127.0.0.1",6379); for (int i = 0; i <1000; i++) { jedis.set("key"+i, "Hello"+i); System.out.println("設置key"+i+"的數據到redis"); Thread.sleep(2); } //執行保存,會在服務器下生成一個dump.rdb數據庫文件 jedis.save(); jedis.close(); System.out.println("寫入完成"); }
運行結果:
這裏的save方法是同步的,沒有寫入完成前不執行後面的代碼。
5.六、異步寫入
//異步保存 public static void asyncSave() throws Exception{ Jedis jedis=new Jedis("127.0.0.1",6379); for (int i = 0; i <1000; i++) { jedis.set("key"+i, "Hello"+i); System.out.println("設置key"+i+"的數據到redis"); Thread.sleep(2); } //執行異步保存,會在服務器下生成一個dump.rdb數據庫文件 jedis.bgsave(); jedis.close(); System.out.println("寫入完成"); }
若是數據量很是大,要保存的內容不少,建議使用bgsave,若是內容少則可使用save方法。關於各方式的比較源自網友的博客。
一、Redis的第一個持久化策略:RDB快照
Redis支持將當前數據的快照存成一個數據文件的持久化機制。而一個持續寫入的數據庫如何生成快照呢。Redis藉助了fork命令的copy on write機制。在生成快照時,將當前進程fork出一個子進程,而後在子進程中循環全部的數據,將數據寫成爲RDB文件。
咱們能夠經過Redis的save指令來配置RDB快照生成的時機,好比你能夠配置當10分鐘之內有100次寫入就生成快照,也能夠配置當1小時內有1000次寫入就生成快照,也能夠多個規則一塊兒實施。這些規則的定義就在Redis的配置文件中,你也能夠經過Redis的CONFIG SET命令在Redis運行時設置規則,不須要重啓Redis。
Redis的RDB文件不會壞掉,由於其寫操做是在一個新進程中進行的,當生成一個新的RDB文件時,Redis生成的子進程會先將數據寫到一個臨時文件中,而後經過原子性rename系統調用將臨時文件重命名爲RDB文件,這樣在任什麼時候候出現故障,Redis的RDB文件都老是可用的。
同時,Redis的RDB文件也是Redis主從同步內部實現中的一環。
可是,咱們能夠很明顯的看到,RDB有它的不足,就是一旦數據庫出現問題,那麼咱們的RDB文件中保存的數據並非全新的,從上次RDB文件生成到 Redis停機這段時間的數據所有丟掉了。在某些業務下,這是能夠忍受的,咱們也推薦這些業務使用RDB的方式進行持久化,由於開啓RDB的代價並不高。 可是對於另一些對數據安全性要求極高的應用,沒法容忍數據丟失的應用,RDB就無能爲力了,因此Redis引入了另外一個重要的持久化機制:AOF日誌。
二、Redis的第二個持久化策略:AOF日誌
AOF日誌的全稱是Append Only File,從名字上咱們就能看出來,它是一個追加寫入的日誌文件。與通常數據庫不一樣的是,AOF文件是可識別的純文本,它的內容就是一個個的Redis標準命令。好比咱們進行以下實驗,使用Redis2.6 版本,在啓動命令參數中設置開啓AOF功能:
./redis-server --appendonly yes
而後咱們執行以下的命令:
redis 127.0.0.1:6379>
set
key1 Hello
OK
redis 127.0.0.1:6379> append key1
" World!"
(integer) 12
redis 127.0.0.1:6379> del key1
(integer) 1
redis 127.0.0.1:6379> del non_existing_key
(integer) 0
這時咱們查看AOF日誌文件,就會獲得以下內容:
$ cat appendonly.aof
*2
$6
SELECT
$1
0
*3
$3
set
$4
key1
$5
Hello
*3
$6
append
$4
key1
$7
World!
*2
$3
del
$4
key1
能夠看到,寫操做都生成了一條相應的命令做爲日誌。其中值得注意的是最後一個del命令,它並無被記錄在AOF日誌中,這是由於Redis判斷出 這個命令不會對當前數據集作出修改。因此不須要記錄這個無用的寫命令。另外AOF日誌也不是徹底按客戶端的請求來生成日誌的,好比命令 INCRBYFLOAT 在記AOF日誌時就被記成一條SET記錄,由於浮點數操做可能在不一樣的系統上會不一樣,因此爲了不同一份日誌在不一樣的系統上生成不一樣的數據集,因此這裏只將操做後的結果經過SET來記錄。
AOF重寫
你能夠會想,每一條寫命令都生成一條日誌,那麼AOF文件是否是會很大?答案是確定的,AOF文件會愈來愈大,因此Redis又提供了一個功能,叫作AOF rewrite。其功能就是從新生成一份AOF文件,新的AOF文件中一條記錄的操做只會有一次,而不像一份老文件那樣,可能記錄了對同一個值的屢次操做。其生成過程和RDB相似,也是fork一個進程,直接遍歷數據,寫入新的AOF臨時文件。在寫入新文件的過程當中,全部的寫操做日誌仍是會寫到原來老的 AOF文件中,同時還會記錄在內存緩衝區中。當重完操做完成後,會將全部緩衝區中的日誌一次性寫入到臨時文件中。而後調用原子性的rename命令用新的 AOF文件取代老的AOF文件。
2、Redis持久化性能是否可靠?
從上面的流程咱們可以看到,RDB是順序IO操做,性能很高。而同時在經過RDB文件進行數據庫恢復的時候,也是順序的讀取數據加載到內存中。因此也不會形成磁盤的隨機讀取錯誤。
而AOF是一個寫文件操做,其目的是將操做日誌寫到磁盤上,因此它也一樣會遇到咱們上面說的寫操做的5個流程。那麼寫AOF的操做安全性又有多高呢?實際上這是能夠設置的,在Redis中對AOF調用write寫入後,什麼時候再調用fsync將其寫到磁盤上,經過appendfsync選項來控制,下面appendfsync的三個設置項,安全強度逐漸變強。
一、appendfsync no
當設置appendfsync爲no的時候,Redis不會主動調用fsync去將AOF日誌內容同步到磁盤,因此這一切就徹底依賴於操做系統的調試了。對大多數Linux操做系統,是每30秒進行一次fsync,將緩衝區中的數據寫到磁盤上。
二、appendfsync everysec
當設置appendfsync爲everysec的時候,Redis會默認每隔一秒進行一次fsync調用,將緩衝區中的數據寫到磁盤。可是當這一 次的fsync調用時長超過1秒時。Redis會採起延遲fsync的策略,再等一秒鐘。也就是在兩秒後再進行fsync,這一次的fsync就無論會執行多長時間都會進行。這時候因爲在fsync時文件描述符會被阻塞,因此當前的寫操做就會阻塞。
因此,結論就是:在絕大多數狀況下,Redis會每隔一秒進行一次fsync。在最壞的狀況下,兩秒鐘會進行一次fsync操做。
這一操做在大多數數據庫系統中被稱爲group commit,就是組合屢次寫操做的數據,一次性將日誌寫到磁盤。
三、appednfsync always
當設置appendfsync爲always時,每一次寫操做都會調用一次fsync,這時數據是最安全的,固然,因爲每次都會執行fsync,因此其性能也會受到影響。
對於pipelining有什麼不一樣?
對於pipelining的操做,其具體過程是客戶端一次性發送N個命令,而後等待這N個命令的返回結果被一塊兒返回。經過採用pipilining 就意味着放棄了對每個命令的返回值確認。因爲在這種狀況下,N個命令是在同一個執行過程當中執行的。因此當設置appendfsync爲everysec 時,可能會有一些誤差,由於這N個命令可能執行時間超過1秒甚至2秒。可是能夠保證的是,最長時間不會超過這N個命令的執行時間和。
3、和其它數據庫的比較
上面操做系統層面的數據安全咱們已經講了不少,其實,不一樣的數據庫在實現上都大同小異。總之,最後的結論就是,在Redis開啓AOF的狀況下,其單機數據安全性並不比這些成熟的SQL數據庫弱。
在數據導入方面的比較
這些持久化的數據有什麼用,固然是用於重啓後的數據恢復。Redis是一個內存數據庫,不管是RDB仍是AOF,都只是其保證數據恢復的措施。因此 Redis在利用RDB和AOF進行恢復的時候,都會讀取RDB或AOF文件,從新加載到內存中。相對於MySQL等數據庫的啓動時間來講,會長不少,由於MySQL原本是不須要將數據加載到內存中的。
可是相對來講,MySQL啓動後提供服務時,其被訪問的熱數據也會慢慢加載到內存中,一般咱們稱之爲預熱,而在預熱完成前,其性能都不會過高。而Redis的好處是一次性將數據加載到內存中,一次性預熱。這樣只要Redis啓動完成,那麼其提供服務的速度都是很是快的。
而在利用RDB和利用AOF啓動上,其啓動時間有一些差異。RDB的啓動時間會更短,緣由有兩個,一是RDB文件中每一條數據只有一條記錄,不會像 AOF日誌那樣可能有一條數據的屢次操做記錄。因此每條數據只須要寫一次就好了。另外一個緣由是RDB文件的存儲格式和Redis數據在內存中的編碼格式是一致的,不須要再進行數據編碼工做。在CPU消耗上要遠小於AOF日誌的加載。
轉載於bcombetter的Redis提供的持久化機制(RDB和AOF)