【Redis系列6】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
在這裏插入圖片描述
一旦我們執行了一些如setsadd等能修改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的同時加上過期時間,這樣可以保證設值和設過期時間的原子性。

最後我們可以通過ttlpttl兩個命令來查詢剩餘過期時間:

  • 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的內存淘汰策略,着重介紹了LRULFU兩種算法,最後我們介紹了Redis的兩種持久化機制,並分別介紹了RDBAOF兩種持久化機制的優缺點。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章