入選數據庫頂會 VLDB:如何有效降低產品級內存數據庫快照尾延遲?

阿里雲操作系統團隊、阿里雲數據庫團隊以及上海交通大學新興並行計算研究中心一起合作的論文 “Async-fork: Mitigating Query Latency Spikes Incurred by the Fork-based Snapshot Mechanism from the OS Level” 被數據庫系統領域頂會 Very Large Data Bases Conferences (VLDB)錄用爲長論文。VLDB 從 1975 年開始舉辦,屬於計算機數據庫系統領域頂級會議(CCF A 類),平均錄用率爲 18.6%。

摘要

得益於超快的查詢速度,內存鍵值對數據庫(In-memory key-value store, 下稱 IMKVS)被廣泛使用於延遲敏感的在線應用程序中。

爲了支持數據持久化,流行的 IMKVS(例如 Redis 與 KeyDB)使用系統調用 fork 定期獲取內存數據的快照。然而,這種基於 fork 的快照機制可能會引發在快照生成期間到達的查詢延遲大幅增加,進而影響在線程序的服務質量。這是因爲 IMKVS 引擎在調用 fork 期間陷入內核態,爲了保證數據的一致性,在此期間無法處理用戶查詢請求。而當 IMKVS 內存佔用較大時,fork 調用過程耗時非常長。

爲此,我們優化了 fork,這可以在不修改 IMKVS 的基礎上,解決快照期間查詢延遲大幅增加的問題。

具體而言,我們設計了一個新的 fork(稱爲 Async-fork),將 fork 調用過程中最耗時的頁表拷貝部分從父進程移動到子進程,父進程因而可以快速返回用戶態處理用戶查詢,子進程則在此期間完成頁表拷貝。爲了保持父子進程之間的數據一致性,我們設計了一套高效的主動同步機制。實驗結果表明,與 Linux 中的默認 fork 相比,Async-fork 顯著減少了快照期間到達請求的尾延遲(在 8GB IMKVS 實例上,p99 延遲減少了 81.76%;在 64GB 的實例上 p99 延遲減少了 99.84%)。

問題介紹

對於 IMKVS 而言,由於所有數據都駐留在內存中,數據持久化是實現數據備份與容災的關鍵功能。流行的 IMKVS(Redis 與 KeyDB)實現數據持久化的方式是使用系統調用 fork 獲取內存中數據在某時間點上的快照,並將快照轉儲到硬盤中。

如圖 1(a)所示,父進程調用 fork 創建子進程,而子進程保有與父進程在調用 fork 的時刻相同的數據。隨後,子進程在後臺將數據寫入硬盤,而父進程則繼續處理用戶的查詢請求。儘管這樣的快照機制已經將繁重的 IO 任務從父進程委派給子進程,但仍然可能導致在 fork 調用期間到達的請求的延遲激增(例如,圖 1(a)中在 T0 時刻到達的請求)。

這是因爲父進程在調用 fork 期間陷入內核態,無法處理用戶查詢請求。因爲 fork 調用的耗時隨進程內存的佔用增大而增大,延遲激增的現象隨 IMKVS 內存佔用的增大而變得更爲嚴重。實驗表明,在 64GB 的 Redis 實例上,非快照期間請求的 p99 延遲與最大延遲分別爲 0.08ms 與 6.46ms,而在快照期間請求的 p99 延遲與最大延遲分別爲 911.95ms 與 1204.78ms。

圖 1:The workflow of the parent and child process with(a)default fork,(b)shared page table(SPT)-based fork,(c)the proposed Async-fork in the snapshot procedure.

在默認 fork 的調用過程中,父進程需要將許多進程元數據(例如文件描述符、信號量、頁表等)複製到子進程,而頁表的複製是其中最耗時的部分(佔據 fork 調用耗時的 97% 以上)。先前的研究者提出了基於共享頁表的 fork 優化方法,這種方法建議以寫時複製(Copy-on-write, 下稱 CoW)的方式在父子進程之間共享頁表。

如圖 1(b)所示,在共享頁表方案中,頁表複製不在 fork 調用期間進行,而是在將來發生修改時再進行復制。雖然共享頁表能夠很大程度上解決延遲激增問題,但我們的分析表明這樣的設計在 IMKVS 中導致父進程從 fork 調用中返回用戶態後頻繁中斷陷入內核態完成頁表複製,對性能造成了不利影響。此外,共享頁表還引入了數據泄漏漏洞,這個漏洞可能導致子進程持有與父進程不一致的數據,破壞了快照一致性。因此,共享頁表方法不適用於解決延遲激增問題。

Async-fork 的核心思想與挑戰

我們提出了另一種 fork 的優化方法(稱爲 Async-fork),總體設計思路如圖 1(c)所示。Async-fork 設計的核心思想是將複製頁表的工作從父進程轉移到子進程。這可以縮短父進程調用 fork 時陷入內核態的時間,能夠儘快返回用戶態處理用戶的查詢請求,從而解決延遲激增問題;另一方面,還確保了父進程和子進程都具有獨佔頁表,從而避免引入數據泄漏漏洞。

然而,實現上述設計並非易事。如圖 2 所示,我們面臨的首要挑戰是頁表的異步操作可能導致快照不一致。以圖 2 中的①爲例,IMKVS 在 T0 時刻拍攝快照,而某個用戶請求在 T2 時刻向 IMKVS 插入了新的鍵值對(k2, v2),這導致父進程修改它的頁表項(PTE2)。當這個被修改的頁表項尚未被子進程複製時,新插入的鍵值對將被子進程最終寫入硬盤,破壞了快照一致性。因此,我們在 Async-fork 中設計了一種主動同步機制,使父進程在修改頁表項前,主動將被修改的頁表項複製到子進程。

實際上,除了用戶請求可能導致頁表項修改外,操作系統中的許多內存操作都會引起頁表項修改。例如,操作系統會定期在 NUMA 節點之間遷移頁面,導致涉及的頁表項被修改爲不可訪問(圖 2 中的②)。因此,主動同步機制必須捕獲父進程中所有可能的頁表項修改操作,以嚴格保證一致性。

另一方面,Async-fork 執行的過程中可能會出現錯誤。例如,由於內存不足,子進程可能無法複製頁表項(圖 2 中的③)。因此,我們在 Async-fork 中加入了錯誤處理。如果 Async-fork 執行的過程中發生錯誤,我們保證將父進程恢復到它調用 Async-fork 之前的狀態。

圖 2:The challenges in Async-fork.

Async-fork 的設計詳解

在詳解 Async-fork 的設計之前,首先簡要介紹 Linux 虛擬內存的相關知識。進程有自己的虛擬內存空間,Linux 使用一組虛擬內存區域(Virtual memory area, 下稱 VMA)來描述進程的虛擬內存空間。一個 VMA 描述虛擬內存空間中的一片連續區域。頁表是用於將虛擬內存映射到物理內存的數據結構。頁表由若干頁表項(Page table entry,下稱 PTE)組成,PTE 維護着虛擬地址到物理地址的轉換信息和訪問權限。

爲了降低內存成本,頁表以多級基數樹的方式存儲,而 PTE 位於最後一級的葉節點,如圖 3 所示。頁表最多有五級,從上到下依次爲 PGD 級、P4D 級、PUD 級、PMD 級和 PTE 級。由於 P4D 通常是禁用的,因此我們在本文中僅關注其他四級。每級中的一個節點是一個物理頁,它是一個存儲了若干項的表。每個項包含一個物理頁的地址,而該物理頁將作爲下一級節點。當頁大小設置爲 4KB 時,每個級別中的表包含 512 個項。對於一個 VMA,存在一系列與之對應的 PTE,它們翻譯了這個 VMA 中的所有虛擬地址,我們將這些 PTE 稱爲“VMA 的 PTE”,而“VMA 的 PMD 項”則是樹中這些 PTE 的所有父級 PMD 項的集合。

圖 3:The organization of the page table.

運行過程

算法 1 描述了 Async-fork 中父進程與子進程的運行過程。在默認 fork 中,父進程遍歷每個 VMA,將每個 VMA 複製到子進程,並自上而下地複製該 VMA 對應的頁表項到子進程。在 Async-fork 中,父進程同樣遵循上述過程,但只負責將 PGD、PUD 這兩級的項複製到子進程(Line 1 to Line 5)。

隨後,父進程將子進程放置到某個 CPU 上使子進程開始運行,而父進程返回到用戶態(Line 7 to Line 14),如果在此期間檢測到了 PTE 修改,則會觸發主動同步機制,該機制用以確保 PTE 在修改前被複制到子進程,我們將在下文詳解主動同步機制。對於子進程而言,它遍歷每個 VMA,並從父進程複製 PMD 項和 PTE(Line 15 to Line 24)。

主動同步機制

父進程返回用戶態後,父進程的 PTE 可能被修改。由於只有父進程能夠感知到這些修改,有兩種方式可以確保該 PTE 被修改前被複制到子進程:

1) 父進程主動地將該 PTE 複製到子進程,隨後執行修改。

2) 父進程通知子進程複製該 PTE,等待複製完成,隨後執行修改。由於這兩種方式都會導致父進程等待且等待時間相同,我們選擇了前一種方式。

當一個 PTE 將被修改時(如何檢測 PTE 修改將在下文詳解),我們選擇讓父進程不僅複製這一個 PTE,還同時將位於同一個表上的所有 PTE(一共 512 個 PTE),連同它的父級 PMD 項複製到子進程。以圖 4 爲例,所有 PGD 與 PUD 條目均已被複制到子進程,而父進程中的 PTE1 將被修改。此時,父進程會將 PMD2、PTE0、PTE1 都複製到子進程。

圖 4:An example of copying page table in Async-fork."RW-1"represents writable and“RW=0"represents writeprotected.

避免重複複製——實際上,即使父進程中的 PTE 發生修改,也並不是總是需要將其複製給子進程,因爲子進程可能已經複製過這個 PTE 了。爲避免不必要的複製,我們使用 PMD 項上的 RW 位來標記是否被複制。具體而言,當父進程第一次返回用戶態時,它所有 PMD 項被設置爲寫保護(RW=0),代表這個 PMD 項以及它指向的 512 個 PTE 尚未被複制到子進程。當子進程複製一個 PMD 項時,通過檢查這個 PMD 是否爲寫保護,即可判斷該 PMD 是否已經被複制到子進程。如果尚未被複制,子進程將複製這個 PMD,以及它指向的 512 個 PTE。

在完成這個過程後,子進程將父進程中的該 PMD 設置爲可寫(RW=1),代表這個 PMD 項以及它指向的 512 個 PTE 已經被複制到子進程。當父進程觸發主動同步時,也通過檢查 PMD 項是否爲寫保護判斷是否被複制,並在完成複製後將 PMD 項設置爲可寫。由於在複製 PMD 項和 PTE 時,父進程和子進程都鎖定 PTE 表,因此它們不會同時複製同一 PMD 項指向的 PTE。

檢測 PTE 修改——PTE 的修改既可能由用戶操作導致,也可能由操作系統的內存操作導致。我們將 PTE 的修改分爲兩類:

1)VMA 級的修改。有些操作作用於特定的 VMA,包括創建、合併、刪除 VMA 等。VMA 的修改也可能導致 VMA 的 PTE 被修改。例如,用戶發送查詢刪除大量鍵值對, 然後 IMKVS(父進程)通過 munmap 回收對應的虛擬內存空間。

2) PMD 級的修改。例如,父進程的頁可能因爲內存不足被 OOM killer 回收。在 VMA 級的修改中,多個 PMD 項被涉及,而在 PMD 級的修改中僅有一個 PMD 項被涉及。表 1 總結了這些操作,並列出了這些操作中發生修改行爲的函數(checkpoint)。

表 1:0perations that may modify VMAs/PTEs.

我們在這些函數中插入了代碼實現 PTE 修改檢測。一旦這些函數被執行,父進程檢查修改是否涉及未複製的 PMD 項和 PTE(通過檢查 PMD 是否寫保護),並將未複製的項複製到子進程。在 VMA 級的修改中,如果一個 VMA 描述了相當大的虛擬內存空間,父進程可能會花費相對較長的時間遍歷檢查這個 VMA 的 PMD 項。

爲了減小檢查開銷,我們在父子進程的 VMA 上引入雙向指針,幫助父進程快速判斷一個 VMA 的所有頁表項是否已經複製到子進程。每個 VMA 數據結構中被添加了一個指針,在 Async-fork 調用時由父進程初始化。父進程 VMA 中的指針指向子進程的 VMA,而子進程 VMA 中的指針指向父進程的 VMA,如圖 4 所示。

通過這種方式,雙向指針在父子進程的 VMA 間創建了連接。如果一個 VMA 的所有頁表項都被複制到子進程,則連接被斷開。具體而言,如果在子進程複製一個 VMA 的 PMD 項和 PTE 的過程中,父進程中未發生 VMA 級的修改,則子進程在複製操作完成後斷開連接。否則,意味着父進程中發生了 VMA 級的修改,父進程將負責複製這個 VMA 的 PMD 項和 PTE 到子進程,並在複製完成後斷開連接。

通過這種方式,當父進程中發生 VMA 級的修改時,父進程可以首先檢查該 VMA 的指針連接,以快速判斷該 VMA 的頁表項是否已被全部複製。對於頁表項已經全部被複制的 VMA,父進程不再需要檢查它的所有 PMD 項。雙向指針還用於錯誤處理,將在下文描述。

錯誤處理

Async-fork 在複製頁表時涉及到內存分配,因此會發生錯誤。例如,由於內存不足,進程可能無法申請到新的 PTE 表。當錯誤發生時,我們應該將父進程恢復到它調用 Async-fork 之前的狀態,即確保父進程不會因爲失敗調用了 Async-fork 而發生改變。

在 Async-fork 中,父進程 PMD 項目的 RW 位可能會被修改。因此,當發生錯誤時,我們需要將 PMD 項全部回滾爲可寫。錯誤可能發生在:1) 父進程複製 PGD 項與 PUD 項時,2) 子進程複製 PMD 項與 PTE 時,以及 3) 在父進程觸發主動同步期間。

對於第一種情況,父進程負責回滾所有寫保護的 PMD。對於第二種情況,子進程負責回滾所有剩餘的未複製的 PMD 項目,隨後發送 SIGKILL 信號。當子進程返回用戶態時,將接收到該信號並被殺死。對於第三種情況,父進程僅負責回滾正在複製的 VMA 中的 PMD 項,然後將錯誤代碼存儲到該 VMA 的雙向指針中。子進程在複製 VMA 的 PMD 項與 PTE 之前(或完成複製之後),都會檢查 VMA 中的雙向指針中是否存在錯誤碼。如果子進程遇到錯誤碼,它立即停止複製,並執行第二種情況的錯誤處理行爲。這樣可以避免錯誤處理過程中父子進程之間的鎖爭用。

Async-fork 的實現及優化

圖 5:The implementation of Async-fork.

圖 5 描述了 Async-fork 在 Linux 中的實現。我們向 fork 調用路徑與內存子系統中的一些函數插入了鉤子(hook)函數,鉤子函數的函數體則封裝到內核模塊中。模塊化實現允許維護者在運行時隨時插入、卸載模塊,以隨時修改 Async-fork。鉤子函數由默認 fork 調用路徑上的 copy_page_range 函數改進而來,它通過接收一個參數(Fast 或 Slow)來控制頁表複製行爲。

靈活性——Async-fork 並沒有實現爲一個新的系統調用,而是集成在默認 fork 中,用戶可以在不修改程序原源碼的情況下使用 Async-fork。我們提供了一個接口以供用戶選擇使用默認 fork 或 Async-fork。具體而言,我們將該接口暴露到內存 cgroup 中。當用戶將進程加入內存 cgroup 組後,可以向接口寫入 0(使用默認 fork)或者正整數(使用 Async-fork)以選擇調用 fork 的方式。

額外開銷——Async-fork 引入的額外開銷來自向每個進程中的各 VMA 中添加指針(8B),因此額外的內存開銷是 VMA 的數量乘以 8B,這樣的開銷通常可以忽略不計。

多架構支持——我們將 Async-fork 實現在 x86 與 ARM64 上。事實上,Async-fork 可以實現在任何支持 hierarchical attributes 頁表的架構上。

性能優化——我們注意到當父進程觸發主動同步時,父進程會陷入內核態,從而影響父進程的性能。一種減少主動同步觸發的方法是加速子進程的頁表複製過程,這是因爲主動同步僅可能在子進程複製頁表期間發生。子進程複製頁表的過程越短,父進程觸發主動同步的概率越低。由於進程的 VMA 是相互獨立的,我們可以在子進程中啓用多個內核線程並行複製各 VMA 的頁表項,並獲得接近線性的加速比。啓動多個內核線程可能消耗額外的 CPU 資源,我們因此讓這些線程週期性地檢查是否應該被搶佔並放棄 CPU 資源(通過調用 cond_resched 函數),以減少對其他正常進程的干擾。用戶可以通過向內存 cgroup 組中暴露的接口寫入正整數,以控制啓用的內核線程數量。

實驗數據分析

實驗設置

我們首先在單機上(實驗環境如上表所示)驗證了 Async-fork 的有效性,其次在本節末尾的實驗評估了 Async-fork 在真實生產環境中的效果。我們在流行的 IMKVS(Redis-5.0.10 和 KeyDB-6.2.0)上驗證 Async-fork 的有效性,並選擇了 Redis benchmark 和 Memtier benchmark 模擬多種場景的工作負載。

實驗關注在快照期間到達的請求的尾延遲(p99 延遲和最大延遲)。選取了最新的基於共享頁表的 fork 優化技術 On-demand-fork(ODF)作爲對比。在 ODF 中,由於頁表在父子進程間共享,進程每次對共享數據的修改都會觸發 PMD 項與 PTE 的拷貝(頁表 CoW),本文對 ODF 中的這種開銷進行了分析。本文沒有展示 Async-fork 與系統中默認 fork 進行對比的結果,因爲在所有場景中,Async-fork 都至少十倍優於默認 fork,在部分場景甚至百倍優於默認 fork。

整體性能評估

尾延遲

圖 6:The 99%-ile latency of snapshot queries.

圖 7:The maximum latency of snapshot queries.

圖 6 和圖 7 分別展示了使用 Redis benchmark 生成寫密集負載,在 Redis 和 KeyDB 中使用 ODF 和 Async-fork 生成快照時,快照期間到達請求的 p99 延遲和最大延遲。在 p99 延遲方面,Async-fork 在所有場景中都優於 ODF,並且當實例變大時,兩者的性能差距會增加。例如,在 64GB 實例上運行,使用 ODF 時請求的 p99 延遲爲 3.96ms (Redis) 和 3.24ms (KeyDB),而 Async-fork 將 p99 延遲減少到 1.5ms(Redis,-61.9%)和 1.03ms(KeyDB,- 68.3%)。

在最大延遲方面,Async-fork 依然優於 ODF,即使實例大小爲 1GB。對於一個 1GB 的 IMKVS 實例,使用 ODF 時最大延遲分別爲 13.93ms (Redis) 和 10.24ms (KeyDB),而 Async-fork 將其減少到 5.43ms (Redis,-60.97%) 和 5.64ms (KeyDB, -44.95%)。

圖 8:The frequency of interruptions in the parent prolcess during the snapshot process.

在快照期間,IMKVS(父進程)可能會中斷陷入內核態,停止處理用戶請求,這將損害 IMKVS 性能。Async-fork 的中斷是由於父進程觸發主動同步導致,而 ODF 的中斷是頁表 CoW 引起的。我們使用 bcc 工具統計了快照期間父進程的中斷次數,以及每次中斷的持續時間,以分析 Async-fork 優於 ODF 的原因。bcc 的統計結果以直方圖顯示(圖 8),中斷時長主要落在[16 , 31 ] 和 [32 , 63 ] 兩個區間。

圖 8 (a) 與 (b) 分別展示了不同 Redis 實例大小下,中斷時長落在[16us, 31us]和[32us, 63us]的中斷次數。我們可以發現 Async-fork 顯著減少了父進程進入內核態的中斷次數。例如,在 16GB 實例中,ODF 導致了 7348 此中斷,而將中斷次數降低到 446 次。Async-fork 大大減少了中斷的原因是因爲中斷只有在子進程複製 PMD 項與 PTE 時纔可能發生。而在 ODF 中,在子進程完成數據持久化(向硬盤寫入所有數據)之前,父進程都可能會發生中斷。一般來說,數據持久化的過程需要數十秒,而複製 PMD 項與 PTE 所需的時間不超過 2 秒。在相同的工作負載下,使用 ODF 時父進程更容易中斷。

消融實驗

讀/寫密集場景

圖 9:The 99%-ile latency and maximum latency of snapshot queries under different workloads in an 8GB IMKVS.

圖 9 展示使用 Memtier benchmark 生成不同讀寫模式的四種工作負載的結果。圖中“1:1 (Uni.)”代表讀寫比爲 1:1 的均勻分佈工作負載,而“1:10 (Gau.)”是讀寫比爲 1:10 的高斯分佈工作負載。從圖中可見,Async-fork 在各種負載均優於 ODF。Asycn-fork 帶來的收益在寫請求更多的負載中表現得更爲明顯,這是因爲父進程在讀密集型的工作負載中僅需要複製少量的頁表項。一般來說,Async-fork 更適合寫密集的工作負載。在子進程生命週期內被修改的內存越多,Async-fork 帶來的收益越多。

客戶端連接數

圖 10:The 99%-ile latency and the maximum latency of snapshot queries under different numbers of clients in an 8GB IMKVS.

圖 10 展示了在 Redis benchmark 生成的寫密集負載中使用不同數量客戶端連接 IMKVS 的結果。Async-fork 依然顯著優於 ODF,而性能差距隨着客戶端數量的增加而增加。這是因爲當客戶端數量增加時,更多請求會同時到達 IMKVS,導致更多的 PTE 被修改,疊加排隊效應,父進程中斷的時間會變得更長。

用於並行複製的內核線程數

圖 11:The 99%-ile and maximum latency of snapshot queries when Async-fork uses 1 or 8 threads.

圖 12:(a) The time that the child process takes to copy PMDs/PTEs in Async-fork.(b) The 99%-ile and maximum latency in an 8GB Redis instance.

在 Async-fork 中,可以啓用多個內核線程在子進程中並行複製 PMD 項與 PTE。圖 11 顯示了在不同 Redis 實例大小下快照期間請求的 p99 延遲和最大延遲。Async-fork-#i 代表總共使用 i 個線程並行複製的結果。觀察發現,Async-fork-#1(僅子進程自己)仍然帶來比 ODF 更低的延遲。與 ODF 相比,最大延遲平均降低了 34.3%。

另一方面,使用更多線程(Async-fork-#8)可以進一步降低延遲。這是因爲子進程越早完成 PMD 項與 PTE 的複製,父進程觸發主動同步的概率就越低。圖 12(a) 顯示了子進程使用不同數量的內核線程複製 PMD 項與 PTE 所花費的時間,而圖 12(b) 顯示了 8GB Redis 實例中相應的 p99 延遲和最大延遲。正如我們所見,使用更多的內核線程有效地減少了在子進程執行復制的時間,而這個時間越短則相應的延遲越低。

Fork 調用時長

圖 13:The execution time of Async-fork and ODF.

父進程 fork 調用的持續時間極大地影響了快照期間請求的延遲。圖 13 顯示了父進程在不同 Redis 實例大小下從 Async-fork 和 ODF 調用中返回的時間。在 64GB 的實例中,父進程從 Async-fork 中返回用時 0.61ms,從 ODF 中返回用時 1.1ms(而從默認 fork 中返回用時可達 600ms)。還可以觀察到 Async-fork 的執行比 ODF 稍快,一個可能的原因是 ODF 爲實現頁表 CoW 需要引入額外計數器,從而導致了初始化的額外時間成本。

生產環境中的效果

圖 14:The 99%-ile latency and maximum latency of snap-shot queries with Async-fork in production.

Async-fork 已部署在我們的 Redis 生產環境中。我們在公有云中租用一個 Redis 服務器來評估生產環境中的 Async-fork 使用效果。租用的 Redis 服務器有 16GB 內存和 80GB SSD。我們還租用了另一臺具有 4 個 vCPU 和 16GB 的 ECS 作爲客戶端運行 Redis benchmark。

圖 14 展示了使用默認 fork 和 Async-fork 時,快照期間到達請求的 p99 延遲和最大延遲。對於一個 8GB 的實例,請求的 p99 延遲從 33.29 ms 減少到 4.92 ms,最大延遲從 169.57 ms 減少到 24.63 ms。對於 16GB 的實例,前者從 155.69ms 減少到 5.02ms,而後者從 415.19ms 減少到 40.04ms。

總結

本文關注於內存鍵值對數據庫在使用 fork 拍攝快照時引起的請求尾延遲激增問題,並從操作系統的角度解決了該問題。通過對 fork 的深入分析,本文揭示了它對數據庫請求延遲的造成影響原因,並針對大內存實例的場景提出了優化方法——Async-fork。

Async-fork 設計的核心思想在於將複製頁表的工作從父進程轉移到子進程來。爲保證一致性,我們設計了高效的主動同步機制。Async-fork 在 Linux 內核中實現並部署於 Redis 生產環境中。實驗結果表明,Async-fork 可以顯著降低快照期間的請求尾延遲。與默認 fork 相比,在 8GB 實例中減少 81.76% 的尾延遲,在 64GB 實例中減少 99.84% 的尾延遲。

原文鏈接

本文爲阿里雲原創內容,未經允許不得轉載。

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