[TOC]
# 持久化的方式?
RDB:對內存中數據庫狀態進行快照 。
AOF:把每條寫命令都寫入文件,類似mysql的binlog日志。
# RDB和AOF機制
## RDB持久化
### 1. 工作原理:
* Redis調用fork(),產生一個子進程。
* 子進程把數據寫到一個臨時的RDB文件。
* 當子進程寫完新的RDB文件后,把舊的RDB文件替換掉。
### 2. 觸發機制
RDB觸發持久化分為手動觸發和自動觸發
1. save 命令(手動觸發)
當客戶端向Redis server發送save命令請求進行持久化時,由于Redis是用一個主線程來處理所有,save命令會阻塞Redis server處理其他客戶端的請求,直到數據同步完成。save命令會阻塞Redis服務器進程,直到RDB文件創建完畢為止,在Redis服務器阻塞期間,服務器不能處理任何命令請求,因此線上環境不推薦使用
2. bgsave命令(手動觸發)
與save命令不同,bgsave是異步執行的,當執行bgsave命令之后,Redis主進程會fork 一個子進程將數據保存到rdb文件中,同步完數據之后,對原有文件進行替換,然后通知主進程表示同步完成。
3. 自動觸發
除了手動觸發RDB持久化,Redis內部還存在自動觸發機制,
在配置中集中配置 save m n 的方式,表示 m秒內數據集存在n次修改時,系統自動觸發bgsave 操作。

1. Redis父進程首先判斷:當前是否在執行save,或bgsave/bgrewriteaof(后面會詳細介紹該命令)的子進程,如果在執行則bgsave命令直接返回。bgsave/bgrewriteaof 的子進程不能同時執行,主要是基于性能方面的考慮:兩個并發的子進程同時執行大量的磁盤寫操作,可能引起嚴重的性能問題。
2. 父進程執行fork操作創建子進程,這個過程中父進程是阻塞的,Redis不能執行來自客戶端的任何命令
3. 父進程fork后,bgsave命令返回”Background saving started”信息并不再阻塞父進程,并可以響應其他命令
4. 子進程創建RDB文件,根據父進程內存快照生成臨時快照文件,完成后對原有文件進行原子替換
5. 子進程發送信號給父進程表示完成,父進程更新統計信息
### 5\. 數據恢復 & Redis啟動加載數據
RDB文件的載入工作是在服務器啟動時自動執行的,并沒有專門的命令。但是由于AOF的優先級更高,因此當AOF開啟時,Redis會優先載入AOF文件來恢復數據;
只有當AOF關閉時,才會在Redis服務器啟動時檢測RDB文件,并自動載入。服務器載入RDB文件期間處于阻塞狀態,直到載入完成為止。
所以Redis的內存數據如果很大,會導致數據恢復時間比較長,因此線上實踐更傾向于限制單個Redis的內存不能太大,同時結合Redis Cluster集群使用多節點部署
## AOF持久化
### 1.工作原理
由于需要記錄Redis的每條寫命令,因此AOF不需要觸發, AOF的執行流程包括:
* 命令追加(append):將Redis的寫命令追加到緩沖區aof\_buf;
* 文件寫入(write)和文件同步(sync):根據不同的同步策略將aof\_buf中的內容同步到硬盤;
* 文件重寫(rewrite):定期重寫AOF文件,達到壓縮的目的。

### 2\. AOF 持久化配置

### 3\. AOF同步策略
同步步驟分為兩步:
* Redis收到寫命令后首先會追加到AOF緩沖區aof\_buf,而不是直接寫入文件系統,因為AOF緩沖區是內存提存的,寫入速度極高,可以避免每次寫入命令到硬盤,導致硬盤IO成為Redis的負載瓶頸
* 通過調用系統函數 fsync() 把AOF緩沖區的數據真正寫到磁盤里面持久化。由于數據是先存儲在緩沖區內存里面,如果碰到斷電,宕機那么緩沖區里面的數據沒來得急落盤就會丟失,因此我們必須有一個相對可靠的機制保證數據落盤。
Redis寫命令寫入磁盤的命令是通過appendfsync來配置的。
appendfsync 三個取值代表三種落盤策略:
* `always`:命令寫入aof緩沖區后立即調用系統fsync操作同步到AOF文件,fsync完成后線程返回。這種情況下,每次有寫命令都要同步到AOF文件,硬盤IO成為性能瓶頸。
* `no`:命令寫入aof緩沖區后調用系統write操作,不對AOF文件做fsync同步;同步由操作系統負責,通常同步周期為30秒。這種情況下,文件同步的時間不可控,且緩沖區中堆積的數據會很多,數據安全性無法保證。
* `everysec`:命令寫入aof緩沖區后調用系統write操作,write完成后線程返回;fsync同步文件操作由專門的線程每秒調用一次。everysec是前述兩種策略的折中,是性能和數據安全性的平衡,因此是Redis的默認配置,也是我們推薦的配置。
### 4\. AOF文件重寫(rewrite)
隨著寫操作的不斷增加,AOF文件會越來越大。例如你遞增一個計數器100次,那么最終結果就是數據集里的計數器的值為最終的遞增結果,但是AOF文件里卻會把這100次操作完整的記錄下來。而事實上要恢復這個記錄,只需要1個命令就行了,也就是說AOF文件里那100條命令其實可以精簡為1條。所以Redis支持這樣一個功能:在不中斷服務的情況下在后臺重建AOF文件。

關于文件重寫的流程,有兩點需要特別注意:
(1)重寫由父進程fork子進程進行;
(2)重寫期間Redis執行的寫命令,需要追加到新的AOF文件中,為此Redis引入了aof\_rewrite\_buf緩存。
對照上圖,文件重寫的流程如下:
1. Redis父進程首先判斷當前是否存在正在執行 bgsave/bgrewriteaof的子進程,如果存在則bgrewriteaof命令直接返回,如果存在bgsave命令則等bgsave執行完成后再執行。前面曾介紹過,這個主要是基于性能方面的考慮。
2. 父進程執行fork操作創建子進程,這個過程中父進程是阻塞的。
3.1) 父進程fork后,bgrewriteaof命令返回”Background append only file rewrite started”信息并不再阻塞父進程,并可以響應其他命令。Redis的所有寫命令依然寫入AOF緩沖區,并根據appendfsync策略同步到硬盤,保證原有AOF機制的正確。
3.2) 由于fork操作使用寫時復制技術,子進程只能共享fork操作時的內存數據。由于父進程依然在響應命令,因此Redis使用AOF重寫緩沖區(圖中的aof\_rewrite\_buf)保存這部分數據,防止新AOF文件生成期間丟失這部分數據。也就是說,bgrewriteaof執行期間,Redis的寫命令同時追加到aof\_buf和aof\_rewirte\_buf兩個緩沖區。
4. 子進程根據內存快照,按照命令合并規則寫入到新的AOF文件。
5.1) 子進程寫完新的AOF文件后,向父進程發信號,父進程更新統計信息,具體可以通過info persistence查看。
5.2) 父進程把AOF重寫緩沖區的數據寫入到新的AOF文件,這樣就保證了新AOF文件所保存的數據庫狀態和服務器當前狀態一致。
5.3) 使用新的AOF文件替換老文件,完成AOF重寫。
#### 重寫觸發:
1. 手動觸發:直接調用bgrewriteaof命令,該命令的執行與bgsave有些類似:都是fork子進程進行具體的工作,且都只有在fork時阻塞。
2. 自動觸發:通過配置`auto-aof-rewrite-percentage`和`auto-aof-rewrite-min-size`來完成
`auto-aof-rewrite-percentage 100`:Redis會記住自從上一次重寫后AOF文件的大小(如果自Redis啟動后還沒重寫過,則記住啟動時使用的AOF文件的大小)。如果當前的文件大小比起記住的那個大小超過指定的百分比,則會觸發重寫。
`auto-aof-rewrite-min-size 64mb`:同時需要設置一個文件大小最小值,只有大于這個值文件才會重寫,以防文件很小,但是已經達到百分比的情況。
要禁用自動的日志重寫功能,我們可以把百分比設置為0:
`auto-aof-rewrite-percentage 0`:禁用日志重寫功能
### 5\. 數據恢復 & Redis啟動加載數據
前面提到過,當AOF開啟時,Redis啟動時會優先載入AOF文件來恢復數據;
只有當AOF關閉時,才會載入RDB文件恢復數據。
# AOF和RDB比較
**RDB持久化**
* 優點:RDB文件緊湊,體積小,網絡傳輸快,適合全量復制;恢復速度比AOF快很多。當然,與AOF相比,RDB最重要的優點之一是對性能的影響相對較小。
* 缺點:RDB文件的致命缺點在于其數據快照的持久化方式決定了必然做不到實時持久化,而在數據越來越重要的今天,數據的大量丟失很多時候是無法接受的,因此AOF持久化成為主流。此外,RDB文件需要滿足特定格式,兼容性差(如老版本的Redis不兼容新版本的RDB文件)。
**AOF持久化**
* 優點:支持秒級持久化、兼容性好,最多會丟失 1s 的數據。
* 缺點:文件大、恢復速度慢、對性能影響大。
- 消息隊列
- 為什么要用消息隊列
- 各種消息隊列產品的對比
- 消息隊列的優缺點
- 如何保證消息隊列的高可用
- 如何保證消息不丟失
- 如何保證消息不會重復消費?如何保證消息的冪等性?
- 如何保證消息消費的順序性?
- 基于MQ的分布式事務實現
- Beanstalk
- PHP
- 函數
- 基礎
- 基礎函數題
- OOP思想及原則
- MVC生命周期
- PHP7.X新特性
- PHP8新特性
- PHP垃圾回收機制
- php-fpm相關
- 高級
- 設計模式
- 排序算法
- 正則
- OOP代碼基礎
- PHP運行原理
- zavl
- 網絡協議new
- 一面
- TCP和UDP
- 常見狀態碼和代表的意義以及解決方式
- 網絡分層和各層有啥協議
- TCP
- http
- 二面
- TCP2
- DNS
- Mysql
- 鎖
- 索引
- 事務
- 高可用?高并發?集群?
- 其他
- 主從復制
- 主從復制數據延遲
- SQL的語?分類
- mysqlQuestions
- Redis
- redis-question
- redis為什么那么快
- redis的優缺點
- redis的數據類型和使用場景
- redis的數據持久化
- 過期策略和淘汰機制
- 緩存穿透、緩存擊穿、緩存雪崩
- redis的事務
- redis的主從復制
- redis集群架構的理解
- redis的事件模型
- redis的數據類型、編碼、數據結構
- Redis連接時的connect與pconnect的區別是什么?
- redis的分布式鎖
- 緩存一致性問題
- redis變慢的原因
- 集群情況下,節點較少時數據分布不均勻怎么辦?
- redis 和 memcached 的區別?
- 基本算法
- MysqlNew
- 索引new
- 事務new
- 鎖new
- 日志new
- 主從復制new
- 樹結構
- mysql其他問題
- 刪除
- 主從配置
- 五種IO模型
- Kafka
- Nginx
- trait
- genergtor 生成器
- 如何實現手機掃碼登錄功能
- laravel框架的生命周期