Redis-關於RDB的幾點頓悟-COW(Copy On Write)

摘要

問題概述:

1、RDB的過程中是否會停止對外提供服務?
2、RDB的過程中數據修改了,備份的是修改前的還是修改後的?
3、RDB時是不是先把內容中的所有KV複製一份,保證數據不會被修改?

問題解決:使用Copy On Write 寫時複製

詳細

在看Redis持久化方式中的RDB方式時,想到了幾個問題:

1、 Redis是單線程的,那在RDB的過程中,是不是就沒法對外提供服務了?
Redis操作快的一個重要原因是Redis的數據是在內存中存儲和操作的,持久化本身是磁盤的IO操作,IO操作又是特別耗時的,RDB備份的過程對Redis來說是挺漫長的,如果Redis沒法對外提供服務的話,對Redis的影響是很大的吧;
2、知道備份時不會阻塞對外服務,那在數據備份的過程中,有新的數據變更的操作發生時,備份的是變更前的數據還是變更後的數據呢?
另一個角度:RDB快照的是精確的一個時刻的內存數據呢?還是一段時間內的內存數據?
另一個角度:RDB快照是精確的還是模糊的?
3、既然是數據備份,在開始備份的時候,是不是要把Redis的所有數據現在內存中拷貝一份呢?那樣的話平時Redis服務器的內存利用率就不能大於50%了啊?

解答

1、RDB過程中會fork一個子進程,子進程做數據備份操作,主進程繼續對外提供服務,所有Redis服務不會阻塞;
2、Copy On Write 機制,備份的是開始那個時刻內存中的數據;
3、Copy On Write 機制不需要把整個內存的數據都複製一份;

Copy On Write 機制

核心思路:fork一個子進程,只有在父進程發生寫操作修改內存數據時,纔會真正去分配內存空間,並複製內存數據,而且也只是複製被修改的內存頁中的數據,並不是全部內存數據;

  • Redis中執行BGSAVE命令生成RDB文件時,本質就是調用Linux中的fork()命令,Linux下的fork()系統調用實現了copy-on-write寫時複製;
  • fork()是類Unix操作系統上創建線程的主要方法,fork用於創建子進程(等同於當前進程的副本);
  • 傳統的普通進程複製,會直接將父進程的數據拷貝到子進程中,拷貝完成後,父進程和子進程之間的數據段和堆棧是相互獨立的;
  • copy-on-write技術,在fork出子進程後,與父進程共享內存空間,兩者只是虛擬空間不同,但是其對應的物理空間是同一個;

Linux中CopyOnWrite實現原理

fork()之後,kernel把父進程中所有的內存頁的權限都設爲read-only,然後子進程的地址空間指向父進程。當父子進程都只讀內存時,相安無事。當其中某個進程寫內存時,CPU硬件檢測到內存頁是read-only的,於是觸發頁異常中斷(page-fault),陷入kernel的一箇中斷例程。中斷例程中,kernel就會把觸發的異常的頁複製一份,於是父子進程各自持有獨立的一份。

CopyOnWrite的好處:

1、減少分配和複製資源時帶來的瞬時延遲;
2、減少不必要的資源分配;
CopyOnWrite的缺點:
1、如果父子進程都需要進行大量的寫操作,會產生大量的分頁錯誤(頁異常中斷page-fault);

Redis中的CopyOnWrite

Redis在持久化時,如果是採用BGSAVE命令或者BGREWRITEAOF的方式,那Redis會fork出一個子進程來讀取數據,從而寫到磁盤中。
總體來看,Redis還是讀操作比較多。如果子進程存在期間,發生了大量的寫操作,那可能就會出現很多的分頁錯誤(頁異常中斷page-fault),這樣就得耗費不少性能在複製上。
而在rehash階段上,寫操作是無法避免的。所以Redis在fork出子進程之後,將負載因子閾值提高,儘量減少寫操作,避免不必要的內存寫入操作,最大限度地節約內存。

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