Redis事務ACID特性和內存回收及RDB和AOF持久化原理分析
前言
經過前面相關文章的介紹,我們已經瞭解了Redis當中數據類型的底層存儲結構,對於Redis也可以算是有了基本的認識,那麼從這一篇開始,後面的文章會相繼介紹Redis當中的一些高級特性,比如事務,持久化,發佈/訂閱/事件等等,本文主要會介紹以下兩大特性:
1、Redis當中的事務及其ACID特性
2、Redis的兩種持久化機制:RDB和AOF
事務
提到事務,我想大多數人的第一感覺就是這是關係型數據庫的特性,NoSQL數據庫一般都不具有事務,那麼Redis作爲一款NoSQL數據庫有事務嗎?
Redis居然有事務?
答案是肯定的。Redis當中的單個命令都是原子操作,但是如果我們需要把多個命令組合操作的時候就需要用到事務。
Redis當中,通過下面4個命令來實現事務:
- 1、
multi
:開啓事務 - 2、
exec
:執行事務 - 3、
discard
:取消事務 - 4、
watch
:監視
下圖就是一個完整的事務執行流程:
從上圖中,我們可以總結出Redis的事務主要分爲以下3步:
- 1、執行命令
multi
開啓一個事務 - 2、開啓事務之後執行的命令都會被放入一個隊列,並且固定返回"QUEUED"
- 3、執行命令
exec
提交事務之後,會依次執行隊列裏面的命令,並依次返回所有命令結果(如果想要放棄事務,可以執行discard
命令)。
Redis事務實現原理
Redis中每個客戶端都有自己的事務狀態multiState
,下面就是一個客戶端client
的數據結構定義:
typedef struct client {
uint64_t id;//客戶端唯一id
multiState mstate; //MULTI和EXEC狀態(即事務狀態)
//...省略其他屬性
} client;
multiState
數據結構定義如下:
typedef struct multiState {
multiCmd *commands;//存儲命令的FIFO隊列
int count;//命令總數
//...省略了其他屬性
} multiState;
multiCmd
是一個隊列用來接收並存儲開啓事務之後發送的命令,其數據結構定義如下:
typedef struct multiCmd {
robj **argv;//用來存儲參數的數組
int argc;//參數的數量
struct redisCommand *cmd;//命令指針
} multiCmd;
我們以上面事務的示例截圖中事務爲例,可以得到如下所示的一個簡圖:
Redis事務ACID特性
傳統的關係型數據庫中,一個事務一般都具有ACID特性,想要詳細瞭解事務特性的可以點擊這裏。那麼現在就讓我們來分析一下Redis是否也滿足這ACID四大特性。
A-原子性
在討論原子性之前,我們先來看2個例子:
例子一:執行事務前報錯(執行exec
命令前):
例子二:執行事務的時候報錯(執行exec
命令時):
通過上面兩個例子我們發現,如果我們開啓事務之後,命令在進入隊列之間就報錯了,那麼事務將會被取消,而一旦命令成功進入隊列之後,單個命令的報錯就不會影響其他命令的執行,也就是說Redis中的事務並不會回滾。
Redis中的事務爲什麼不會滾
Redis官網中對這個問題給出了明確的解釋:
總結起來主要就是3個原因:
- 1、Redis作者認爲發生事務回滾的原因大部分都是程序錯誤導致,這種情況一般發生在開發階段,而生產環境很少出現。
- 2、對於邏輯性錯誤,比如本來應該把一個數加1,但是程序邏輯寫成了加2,那麼這種錯誤也是無法通過事務回滾來進行解決。
- 3、Redis追求的是簡單高效,而傳統事務的實現相對比較複雜,這和Redis的設計思想相違背。
C-一致性
一致性指的就是事務執行前後的數據符合數據庫的定義和要求。這一點Redis是符合要求的,上面講述原子性的時候已經提到,不論是發生語法錯誤還是運行時錯誤,錯誤的命令均不會被執行。
I-隔離性
事務中的所有命令都會按順序執行,在執行Redis事務的過程中,另一個客戶端發出的請求不可能被服務,這保證了命令是作爲單獨的獨立操作執行的。所以Redis當中的事務是符合隔離性要求的。
D-持久性
如果Redis當中沒有被開啓持久化,那麼就是純內存運行的,一旦重啓,所有數據都會丟失,所以不具備持久性,而如果Redis開啓了持久化,那麼也需要看開啓的持久化模式是RDB還是AOF,還要視具體配置具體分析,這一點我們後面講述持久化的時候會專門分析。
WATCH命令
上面我們講述事務的時候還提到了一個WATCH命令,這個又是做什麼用的呢?我們還是先來看一個例子。
首先打開客戶端1,並開啓事務:
上面事務中,如果這時候去執行exec
,那麼正常是第一句話返回nil
,第二句話ok
,第三句話lonely_wolf
。
但是這個時候我在另一個客戶端2執行一個set name zhangsan
命令:
執行成功,這時候再返回到客戶端1執行exec
命令:
可以發現,第一句話返回了zhangsan
,也就是說,name這個key值在入隊之後到exec
之前發生了變化,這種在有些場景可能會導致數據被覆蓋等問題的發生,那麼如何解決呢?這時候watch
命令就可以閃亮登場了。
watch命令的作用
watch
命令可以爲Redis事務提供CAS樂觀鎖行爲,它可以在exec
命令執行之前,監視任意key值的變化,也就是說當多個線程更新同一個key值的時候,會跟原值做比較,一旦發現它被修改過,則拒絕執行命令,並且會返回nil給客戶端。
下面還是通過一個示例來演示一下:
首先客戶端1監視key值name,然後開啓事務:
客戶端2執行set name zhangsan
命令:
這時候客戶端1再提交事務,會發現,事務中所有的命令都沒有被執行(也就是說,只要檢測到一個key值被修改過,那麼整個事務都不會被執行):
watch原理分析
下面是一個Redis服務的數據結構定義:
typedef struct redisDb {
dict *watched_keys; /* WATCHED keys for MULTI/EXEC CAS */
int id; /* Database ID */
//省略了其他屬性
} redisDb;
可以看到,redisDb中的watched_keys
存儲了一個字典,這個字典當中的key存的就是被監視的key,然後字典的值存的就是客戶端id
然後每個客戶端還有一個標記屬性CLIENT_DIRTY_CAS
:
一旦我們執行了一些如set
,sadd
等能修改key值對應value的命令,那麼CLIENT_DIRTY_CAS
標記將會被修改,後面執行事務提交命令exec
時一旦發現這個標記被修改過,則會拒絕執行事務。
內存回收
Redis當中我們可以通過4個命令來給一個鍵設置過期時間:
expire key ttl
:將key值的過期時間設置爲ttl
秒pexpire key ttl
:將key值的過期時間設置爲ttl
毫秒expireat key timestamp
:將key值的過期時間設置爲指定的timestamp
秒數pexpireat key timestamp
:將key值的過期時間設置爲指定的timestamp
毫秒數
PS:不管使用哪一個命令,最終Redis底層都是使用pexpireat
命令來實現的,另外,set
等命令也可以設置key的同時加上過期時間,這樣可以保證設值和設過期時間的原子性。
最後我們可以通過ttl
和pttl
兩個命令來查詢剩餘過期時間:
ttl key
返回key剩餘過期秒數,pttl key
返回key剩餘過期的毫秒數
如果未設置過期時間則上面兩個命令返回-1,如果設置了一個非法的過期時間,則都返回-2。
過期策略
如果將一個過期的鍵刪除,我們一般都會有三種策略:
- 1、定時刪除:爲每個鍵設置一個定時器,一旦過期時間到了,則將鍵刪除。這種策略對內存很友好,但是對CPU不友好。因爲每個定時器都會佔用一定的CPU資源。
- 2、惰性刪除:不管鍵有沒有過期都不主動刪除,等到每次去獲取鍵時再判斷是否過期,如果過期就刪除該鍵,否則返回鍵對應的值。這種策略對內存不夠友好,可能會浪費很多內存。
- 3、定期掃描:系統每隔一段時間就定期掃描一次,發現過期的鍵就進行刪除。這種策略相對來說是上面兩種策略的折衷方案,但是這個定期的頻率需要結合實際情況掌控好,但是這種方案也可能會出現過期的鍵也被返回。
在Redis當中,其選擇的是策略2和策略3的綜合使用。不過Redis的定期掃描只會掃描設置了過期時間的鍵,因爲設置了過期時間的鍵Redis會單獨存儲,所以不會出現掃描所有鍵的情況:
typedef struct redisDb {
dict *dict; //所有的鍵值對
dict *expires; //設置了過期時間的鍵值對
dict *blocking_keys; //被阻塞的key,如客戶端執行BLPOP等阻塞指令時
dict *watched_keys; /* WATCHED keys for MULTI/EXEC CAS */
int id; /* Database ID */
//省略了其他屬性
} redisDb;
淘汰策略
假如Redis當中所有的鍵都沒有過期,而且此時內存滿了,那麼客戶端繼續執行set
等命令時Redis會怎麼處理呢?Redis當中提供了不同的淘汰策略來處理這種場景。
首先Redis提供了一個參數maxmemory
來配置Redis最大使用內存
maxmemory <bytes>
或者也可以通過命令config set maxmemory 1GB
來動態修改。
如果沒有設置該參數,那麼在32位的操作系統中最多使用3GB內存,而在64位的操作系統中不作限制。
Redis中提供了8種淘汰策略,通過參數maxmemory-policy
進行配置:
淘汰策略 | 說明 |
---|---|
volatile-lru | 根據LRU算法刪除設置了過期時間的鍵,直到騰出可用空間。如果沒有可刪除的鍵對象,且內存還是不夠用時,則報錯 |
allkeys-lru | 根據LRU算法刪除所有的鍵,直到騰出可用空間。如果沒有可刪除的鍵對象,且內存還是不夠用時,則報錯 |
volatile-lfu | 根據LFU算法刪除設置了過期時間的鍵,直到騰出可用空間。如果沒有可刪除的鍵對象,且內存還是不夠用時,則報錯 |
allkeys-lfu | 根據LFU算法刪除所有的鍵,直到騰出可用空間。如果沒有可刪除的鍵對象,且內存還是不夠用時,則報錯 |
volatile-random | 隨機刪除設置了過期時間的鍵,直到騰出可用空間。如果沒有可刪除的鍵對象,且內存還是不夠用時,則報錯 |
allkeys-random | 隨機刪除所有鍵,直到騰出可用空間。如果沒有可刪除的鍵對象,且內存還是不夠用時,則報錯 |
volatile-ttl | 根據鍵值對象的ttl 屬性, 刪除最近將要過期數據。 如果沒有,則直接報錯 |
noeviction | 默認策略,不作任何處理,直接報錯 |
PS:淘汰策略也可以根據命令config set maxmemory-policy <策略>
進行動態刪除
LRU算法
LRU:Least Recently Used,即:最近最長時間未被使用。這個主要針對的是使用時間。
Redis改進後的LRU算法
在Redis當中,並沒有採用傳統的LRU算法,因爲傳統的LRU算法存在2個問題:
- 1、需要額外的空間進行存儲。
- 2、可能存在某些key值使用很頻繁,但是最近沒被使用,從而被LRU算法刪除。
爲了避免以上2個問題,Redis當中對傳統的LRU算法進行了改造,通過抽樣的方式進行刪除。
配置文件中提供了一個屬性maxmemory_samples 5
,默認值就是5,表示隨機抽取5個key值,然後對這5個key值按照LRU算法進行刪除,所以很明顯,key值越大,刪除的準確度越高。
對抽樣LRU算法和傳統的LRU算法,Redis官網當中有一個對比圖:
- 淺灰色帶是被刪除的對象。
- 灰色帶是未被刪除的對象。
- 綠帶是添加的對象
左上角第一幅圖代表的是傳統LRU算法,可以看到,當抽樣數達到10個(右上角),已經和傳統的LRU算法非常接近了。
Redis如何管理熱度數據
前面我們講述字符串對象的SDS原理時,提到了redisObject
對象中存在一個lru屬性:
typedef struct redisObject {
unsigned type:4;//對象類型(4位=0.5字節)
unsigned encoding:4;//編碼(4位=0.5字節)
unsigned lru:LRU_BITS;//記錄對象最後一次被應用程序訪問的時間(24位=3字節)
int refcount;//引用計數。等於0時表示可以被垃圾回收(32位=4字節)
void *ptr;//指向底層實際的數據存儲結構,如:SDS等(8字節)
} robj;
這個註釋上寫了,這個值是相對於全局變量lru_clock而言的
lru屬性是創建對象的時候會寫入,對象被訪問到時也會進行更新。正常人的思路就是最後決定要不要刪除他肯定是用當前時間戳減去lru,差值最大的就優先被刪除。
但是Redis裏面並不是這麼做的,Redis中維護了一個全局屬性lru_clock
,這個屬性是通過一個全局函數serverCron
每隔100毫秒執行一次來更新的,記錄的是當前unix時間戳,
最後決定刪除的數據是通過lru_clock全局屬性
減去對象的lru屬性
得出的。那麼爲什麼Redis要這麼做呢?直接取全局時間不是更準確嗎?這是因爲這麼做可以避免每次更新對象的lru屬性的時候可以直接取全局屬性,而不需要去調用系統函數來獲取系統時間,從而提升效率(Redis當中有很多這種細節考慮來提升性能,可以說是對性能儘可能的優化到極致)。
不過這裏還有一個問題,我們看到,redisObject
對象中的lru屬性只有24位,24位只能存儲194天的時間戳大小,一旦超過194天之後就會重新從0開始計算,所以這時候就會出現redisObject
對象中的lru
屬性大於全局的lru_clock
屬性的情況。
正因爲如此,計算的時候也需要分爲2種情況,下面就是源碼的計算方式(evict.c內):
- 1、當全局
lruclock
>lru
,則使用lruclock
-lru
得到空閒時間。 - 2、當全局
lruclock
<lru
,則使用lruclock_max
-lru
+lruclock
得到空閒時間。
需要注意的是,這種計算方式並不能保證抽樣的數據中一定能刪除空閒時間最長的。
這是因爲首先超過194天還不被使用的情況很少,再次只有lruclock
第2輪繼續超過lru
屬性時,計算纔會出問題。比如對象A記錄的lru
是1天,而lruclock
第二輪都到10天了,這時候就會導致計算結果只有10-1=9天,實際上應該是194+10-1=203天。但是這種情況可以說又是更少發生,所以說這種處理方式是可能存在刪除不準確的情況,但是我們只需要達到基本準確就可以了。
LFU算法
LFU:Least Frequently Used,即:最近最少頻率使用,這個主要針對的是使用頻率。
這個屬性也是記錄在redisObject
中的lru屬性內。
當我們採用LFU回收策略時,lru
屬性的高16位用來記錄訪問時間(last decrement time:ldt,單位爲分鐘),低8位用來記錄訪問頻率(logistic counter:logc),簡稱counter。
訪問頻次遞增
LFU計數器每個鍵只有8位,它能表示的最大值是255,所以Redis使用的是一種基於概率的對數器來實現counter
的遞增。
給定一箇舊的訪問頻次,當一個鍵被訪問時,counter
按以下方式遞增:
- 1、提取0和1之間的隨機數R
- 2、概率P計算爲
1/(old_value*lfu_log_factor+1)
。 - 3、當
R<P
時,頻次進行遞增
公式中的lfu_log_factor
稱之爲對數因子,默認是10,可以通過參數來進行控制:
lfu_log_factor 10
下圖就是對數因子lfu_log_factor
和頻次counter
增長的關係圖:
可以看到,當對數因子lfu_log_factor
爲100時,10M(1000萬)次訪問纔會將訪問counter
才增長到255,而默認的10也能支持到1M(100萬)次訪問counter
才能達到255上限,這在大部分場景都是足夠滿足需求的。
訪問頻次遞減
如果訪問頻次counter
只是一直在遞增,那麼遲早會全部都到255,也就是說counter
一直遞增不能完全反應一個key的熱度的,所以當某一個key一段時間不被訪問之後,counter
也需要對應減少。
counter
的減少速度由參數lfu-decay-time
進行控制,默認是1,單位是分鐘,默認值1表示:N分鐘內沒有訪問,counter就要減N。
lfu-decay-time 1
具體算法如下:
- 1、獲取當前時間戳,轉化爲分鐘後取低16位(爲了方便後續計算,這個值記爲
now
)。 - 2、取出對象內的
lru
屬性中的高16位(爲了方便後續計算,這個值記爲ldt
)。 - 3、當
lru
>now
時,默認爲過了一個週期(16位,最大65535),則取差值65535-ldt+now
;當lru
<=now
時,取差值now-ldt
(爲了方便後續計算,這個差值記爲idle_time
)。 - 4、取出配置文件中的
lfu_decay_time
值,然後計算:idle_time / lfu_decay_time
(爲了方便後續計算,這個值記爲num_periods
)。 - 5、最後將
counter
減少:counter - num_periods
看起來這麼複雜,其實計算公式就是一句話,就是取出當前的時間戳對比對象中的lru
屬性,計算出當前多久沒有被訪問到,比如計算得到的結果是100分鐘沒有被訪問,然後再去除配置參數lfu_decay_time
,如果這個配置默認爲1也即是100/1=100,代表100分鐘沒訪問counter
就減少100。
下面3幅圖就是源碼內的主要計算方法
源碼db.c
內:
源碼evict.c
內:
Redis持久化機制
Redis雖然是定義爲一個內存數據庫,但是爲了防止數據丟失,其仍然提供了兩種持久化機制:RDB和AOF。
RDB機制
RDB即:Redis DataBase,是Redis當中默認的持久化方案,當觸發持久化條件時,Redis會生成一個dump.rdb
文件,Redis在重啓的時候就會通過解析dump.rdb
文件進行數據恢復。
RDB機制觸發條件
RDB持久化機制有兩種觸發方式:自動觸發
和手動觸發
。
自動觸發
自動觸發方式也可以分爲三種:
- 1、執行
flushall
命令(flushdb
命令不會觸發)時,不過此時生成的讀,dump文件內的數據是空的(dump文件還會存儲一些頭信息,所以文件本身是有內容的,只是沒有數據),沒有什麼太大的意義。
- 2、執行
shutdown
命令時會觸發生成dump文件。
下面就是我這邊重啓之後的一個例子,數據全部都可以正常恢復:
Redis啓動之後日誌如下,第一行就是顯示了Redis從硬盤中進行了數據恢復:
- 3、通過配置文件自動生成,Redis中配置文件默認配置如下:
save 900 1 //900秒內至少有1個key被添加或者更新
save 300 10 //300秒內至少有10個key被添加或者更新
save 60 10000 //60秒內至少有10000個key被添加或者更新
也就是說只要達到這三個條件中的任意一個,就會觸發Redis的RDB持久化機制。
手動觸發
除了自動觸發,Redis中還提供了2個手動觸發RDB機制的命令。
save
:這個命令會阻塞Redis服務器進程,直到成功創建RDB文件,也就是說在生成RDB文件之前,服務器不能處理客戶端發送的任何命令。
bgsave
:父進程會執行fork
操作來創建一個子進程。RDB文件由子進程來負責生成,父進程可以正常處理客戶端發送的命令
如果想要知道上一次成功執行save
或者bgsave
命令的時間,可以執行lastsave
命令進行查看,lastsave
命令返回的是一個unix時間戳。
PS:需要注意的是這兩個命令不能同時被執行,一旦一個命令正在執行中,另一個命令會被拒絕執行。
RDB機制相關配置文件
除了上面提到的觸發生成rdb的配置參數,RDB持久化機制還有如下一些相關命令:
dir
:rdb文件生成目錄。默認是./(當前目錄),可以執行命令:config get dir
進行查看
dbfilename
:rdb文件名。默認是dump.rdb
rdbcompression
:rdb文件是否是LZF壓縮文件。默認是yes
rdbchecksum
:是否開啓數據校驗。默認是yes
RDB機制優點
- 1、RDB是一個非常緊湊的壓縮文件,保存了不同時間點上的文件,非常適合用來災備和數據恢復。
- 2、RDB最大限度地提高了Redis的性能,因爲Redis父進程需要做的唯一的工作就是派生一個子進程來完成剩下的工作。父進程永遠不會執行磁盤I/O或類似的操作。
- 3、與AOP機制想必,RDB方式恢復數據的速度更快
RDB機制缺點
- 1、RDB無法做到實時備份,所以如果Redis停止工作而沒有正確的關機,那麼從上一次備份的到異常宕機的這一段時間的數據將會丟失。
- 2、RDB通常需要父進程來執行fork()操作創建子線程,所以如果頻繁執行fork()的而CPU性能又不是很高的話可能會造成短時間內父進程不可用。
AOF機制
AOF即:Append Only File,是Redis當中提供的另一種持久化機制。AOF採用日誌的形式將每個寫操作追加到文件中。開啓AOF機制後,只要執行更改Redis數據的命令時,命令就會被寫入到AOF文件中。在Redis重啓的時候會根據日誌內容執行一次AOF文件中的命令來恢復數據。
AOF和RDB最大的不同時AOF記錄的是執行命令(類似於MySQL中binlog的statement格式),而RDB記錄的是數據(類似於MySQL中binlog的row格式)。
需要注意的是,假如同時開啓了RDB和AOF兩種機制,那麼Redis會優先選擇AOF持久化文件來進行數據恢復。
下圖就是同時開啓了RDB和AOF兩種機制的情況,Redis選擇了使用AOF機制來進行數據恢復:
AOF機制如何開啓
AOF機制默認是關閉的
appendonly no //是否開啓AOF機制,默認是no
appendfilename "appendonly.aof" //AOF文件名
把appendonly
參數修改爲yes
則可以開啓AOF持久化機制。
PS:和RDB機制一樣,其路徑也是通過dir
配置文件進行配置。
AOF機制數據是否實時寫入磁盤
AOF機制下數據是否實時寫入磁盤,這個和MySQL的redo log機制很類似,也是需要通過參數來進行控制。
AOF數據何時寫入磁盤通過參數appendfsync
來進行控制:
appendfsync | 描述 | Redis作者描述 |
---|---|---|
always | 寫入緩存的同時通知操作系統刷新(fsync)到磁盤(但是也可能會有部分操作系統只是儘快刷盤,而不是實時刷盤) | Slow, Safest |
everysec | 先寫入緩存,然後每秒中刷一次盤(默認值),這種模式極端情況可能會丟失1s的數據 | Compromise |
no | 只寫入緩存,什麼時候刷盤由操作系統自己決定 | Faster |
AOF重寫
AOF機制主要是通過記錄執行命令的方式來實現的,那麼隨着時間的增加,AOF文件不可避免的會越來越大,而且可能會出現很多冗餘命令。比如同一個key值執行了10000次set操作,實際上前面9999次對用戶來說都是沒用的,用戶只需要最後一次執行命令,所以AOF機制就提供了重寫功能。
重寫原理分析
AOF重寫時Redis並不會去分析原有的文件,因爲如果原有文件過大,分析也會很耗時,所以Redi選擇的做法就是重新去Redis中讀取現有的鍵值,然後用一條命令記錄鍵值對的值。
源碼server.h
中定義了1個常量,常量值等於64:
#define AOF_REWRITE_ITEMS_PER_CMD 64
如果在AOF重寫的時候,如果一個集合鍵或者列表鍵或者哈希鍵內包含的元素超過64個,那麼也會採用多條命令來進行重寫。
AOF重寫的時候一般都會有大量的寫操作,所以爲了不阻塞客戶端的命令請求,Redis會把重寫操作放入到子進程中執行,但是放入子進程中執行也會帶來一個問題,那就是重寫期間如果有其他命令被執行了,如何保證數據的一致性?
爲了解決數據不一致問題,Redis中引入了一個AOF重寫緩衝區。當開始執行AOF重寫之後接收到的命令,不但要寫入原本的AOF緩衝區(根據上面提到的參數刷盤),還要同時寫入AOF重寫緩衝區:
一旦子進程完成了AOF文件的重寫,此時會向父進程發出信號,父進程收到信號之後會進行阻塞(阻塞期間不執行任何命令),並進行以下兩項工作:
- 1、將AOF重寫緩衝區的文件刷新到新的AOF文件內
- 2、將新AOF文件進行改名並原子的替換掉舊的AOF文件
完成了上面的兩項工作之後,整個AOF重寫工作完成,父進程開始正常接收命令。
AOF機制觸發條件
AOF機制的觸發條件同樣也分爲自動觸發
和手動觸發
兩種:
- 自動觸發:自動觸發可以通過以下參數進行設置:
auto-aof-rewrite-percentag //文件大小超過上次AOF重寫之後的文件的百分比。默認100,也就是默認達到上一次AOF重寫文件的2倍之後會再次觸發AOF重寫
auto-aof-rewrite-min-size //設置允許重寫的最小AOF文件大小,默認是64M。主要是避免滿足了上面的百分比,但是文件還是很小的情況。
- 手動觸發:執行
bgrewriteaof
命令。
注意:bgrewriteaof命令也不能和上面RDB持久化命令
bgsave`同時執行,否則會創建兩個子進程來同時執行大量寫磁盤操作,影響性能。
AOF機制機制優點
- 1、使用AOF機制,可以自由選擇不同
fsync
(刷盤)策略,而且在默認策略下最多也僅僅是損失1s的數據 - 2、AOF日誌是一個僅追加的日誌,因此如果出現斷電,也不存在查找或損壞問題。即使由於某些原因(磁盤已滿或其他原因),日誌以寫了一半的命令結束,redis-check-aof工具也能夠輕鬆地修復它。
- 3、當AOF變得太大時,Redis能夠在後臺自動重寫。
- 4、不通過與RDB的文件格式,AOF是一種易於理解和解析的格式,依次包含所有操作的日誌。
AOF機制機制缺點
- 1、對於相同的數據集,AOF文件通常比等效的RDB文件大。
- 2、根據
fsync
的具體策略,AOF可能比RDB慢。但是一般情況下,fsync
設置爲每秒的性能仍然很高,禁用fsync
後,即使在高負載下,它的速度也應該和RDB一樣快。 - 3、因爲AOF文件是追加形式,可能會遇到BRPOP、LPUSH等阻塞命令的錯誤,從而導致生成的AOF在重新加載時不能複製完全相同的數據集,而RDB文件每次都是重新從頭創建快照,這在一定程度上來說RDB文件更加健壯。
總結
本文主要介紹了Redis中提供的事務支持,並從傳統事務的角度上分析了Redis事務的ACID特性,然後我們介紹了Redis的內存淘汰策略,着重介紹了LRU
和LFU
兩種算法,最後我們介紹了Redis的兩種持久化機制,並分別介紹了RDB
和AOF
兩種持久化機制的優缺點。