面試官:請說下 Redis 是如何保證在宕機后數據不丟失的
本文轉載自微信公眾號「Java極客技術」,作者鴨血粉絲。轉載本文請聯系Java極客技術公眾號。
持久化
首先我們說下什么是持久化,持久化是將程序數據在持久狀態和瞬時狀態間轉換的機制。通俗的講,就是瞬時數據(比如內存中的數據,是不能永久保存的)持久化為持久數據(比如持久化至數據庫中,能夠長久保存)。另外我們使用的 Redis 之所以快就是因為數據都存儲在內存當中,為了保證在服務器出現異常過后還能恢復數據,所以就有了 Redis 的持久化。
RDB 和 AOF
前面說了什么是持久化,現在說說 Redis 的持久化,眾所周知 Redis 的持久化有兩種方式,一種是快照形式 RDB,另一種是增量文件 AOF。
RDB
RDB 持久化方式是會在一個特定的時間間隔里面保存某個時間點的數據快照,我們拿到這個數據快照過后就可以根據這個快照完整的復制出數據。這種方式我們可以用來備份數據,把快照文件備份起來,傳送到其他服務器就可以直接恢復數據。但是這只是某個時間點的全部數據,如果我們想要最新的數據,就只能定期的去生成快照文件。
RDB 的實現主要是通過創建一個子進程來實現 RDB 文件的快照生成,通過子進程來實現備份功能,不會影響主進程的性能。同時上面也提到 RDB 的快照文件是保存一定時間間隔的數據的,這就會導致如果時間間隔過長,服務器出現異常還沒來得及生成快照的時候就會丟失這個間隔時間的所有數據;那有同學就會說,我們可以把時間間隔設置的短一點,適當的縮短是可以的,但是如果間隔時間段設置短一點頻繁的生成快照對系統還是會有影響的,特別是在數據量大的情況下,高性能的環境下是不允許這種情況出現的。
我們可以在 redis.conf 進行 RDB 的相關配置,配置生成快照的策略,以及日志文件的路徑和名稱。還有定時備份規則,如下圖所示,里面的注釋寫的很清楚,簡單說就是在多少時間以內多少個 key 變化了就會觸發快照。如save 300 10 表示在 5 分鐘內如果有 10 個 key 發生了變化就會觸發生產快照,其他的同理。
除了我們在配置文件中配置自動生成快照文件之外,Redis 本身提供了相關的命令可以讓我們手動生成快照文件,分別是 SAVE 和 BGSAVE ,這兩個命令功能相同但是方式和效果不一樣,SAVE 命令執行完后阻塞服務器進程,阻塞過后服務器就不能處理任何請求,所以在生產上不能用,和SAVE 命令直接阻塞服務器進程的做法不同,BGSAVE 命令是生成一個子進程,通過子進程來創建 RDB 文件,主進程依舊可以處理接受到的命令,從而不會阻塞服務器,在生產上可以使用。
阿粉在這里測試一下自動生成快照,我們修改一下快照的生成策略為save 10 2,然后在本地啟動Redis 服務,并用 redis-cli 鏈接進入,依次步驟如下
1.修改配置,如下:
2.啟動 Redis 服務,我們可以從啟動日志中看到,默認是會先讀取 RDB 文件進行恢復的。
3.鏈接 Redis 服務,并在 10s 內設置 3 個 key。
4.這個時候我們會看到 Redis 的日志里面會輸出下面內容,因為觸發了規則,所以開啟子進程進行數據備份,同時在對應的文件路徑下面,我們也看到了 rdb 文件。
從上面可以看出,我們配置的規則生效了,也成功的生成了 RDB 文件, 后續在服務器出現異常的情況,只要重新啟動就會讀取對應的 RDB 文件進行數據備份。
AOF
AOF 是一種追加執行命令的形式,它跟 RDB 的區別是,AOF 并不是把數據保存下來,而是保存執行的動作。在開啟 AOF 功能的時候,客戶端連接后執行的每一條命令都會被記錄下來。這其實讓阿粉想起來的 MySQL 的 binlog 日志,也是記錄操作的命令,后續可以根據文件去恢復數據。
AOF 是追加命令格式的文件,同樣的我們可以定義多長時間把數據同步一次,Redis 本身提供了三種策略來實現命令的同步,分別是不進行同步,每秒同步一次,以及當有查詢的時候同步一次。默認的策略也是使用最多的策略就是每秒同步一次,這樣我們可以知道,丟失的數據最多也就只有一秒鐘的數據。有了這種機制,AOF 會比 RDB 可靠很多,但是因為文件里面存在的是執行的命令,所以AOF 的文件一般也會比 RDB 的文件大點。
Redis 的 AOF 功能,默認是沒有開啟的,我們可以通過在配置文件中配置appendonly yes 是功能開啟,同時配置同步策略appendfsync everysec 開啟每秒鐘同步一次,我們拿到 AOF 文件過后,可以根據這個文件恢復數據。
同樣的我們在redis.conf 中可以看到默認是沒有開啟 AOF 功能的,并且我們也可以指定對應的文件名稱和路徑。
接下來,我們測試一下開啟 AOF 功能,先修改配置然后重啟 Redis 的服務器,我們會發現已經沒有讀取 RDB 文件的日志了,并且在日志文件路徑下面已經生成了一個 aof 文件。需要注意的是,因為我們重啟的服務,并且開啟了 AOF,所以現在 Redis 服務器里面并沒有我們之前添加的數據(說明什么問題呢?)。
接下來我們使用客戶端連接進入,設置如下值,接下來我們可以看看 aof 文件里面的內容。
我們可以看到aof 文件里面的內容就是執行的命令,只不過是以一種固定的格式存儲的,我們在備份的時候如果不需要哪些數據,可以手動刪掉對應的命令就可以重新備份數據。
文件載入順序
上面我們提到了Redis 的兩種持久化方案,并且兩種方案都會生成對應的文件,那 Redis 在恢復數據的時候是怎么載入文件的呢?并且在兩個文件都存在的情況下,會以哪個為準呢?
通過上面的測試,其實我們可以看出 Redis 是以 AOF 為優先的,畢竟 AOF 相對 RDB 來說在出現異常的情況下保存的數據更加完整。