阿肝正傳之Redis主從同步

pikalu發表於2020-11-18

前言

一門技術光停留在會用的層面遠遠不夠,必須要深入的掌握其原理,然後加以理解和總結,才能叫“會”。

本文是在深入瞭解Redis主從同步原理時,記錄的學習筆記。

目的

Redis提供了主從庫模式,實現了Redis的高可用,保證多個例項的資料一致性。主從庫之間採用的是讀寫分離的方式

讀寫分離的架構

讀操作:主從都可以接受;

寫操作:首先到主庫執行,然後主操作將寫操作同步給從庫。

主從同步的原理

主從庫如何實現第一次同步?

這裡的方法是是replicaof, Redis 5.0以前是 slaveof. 命令如下:

replicaof 主庫IP

主要的操作步驟如下:

  1. 主從庫建立連線、協商同步,主要是為了全量同步做準備。在這一步,從庫和主庫建立起連線,並告訴主庫即將進行同步;主庫確認回覆後,主從庫間就可以開始同步了。

    具體來說,從庫給主庫傳送 psync命令,表示要進行資料同步;主庫根據這個命令的引數來複制,主要包含的引數有:主庫的runID和複製進度offset兩個引數。

    引數說明:

    runID 每個redis例項啟動時都會生成一個隨機ID,用來唯一標記這個例項;當從庫和主庫第一次複製是,因為不知道主庫的runID,所以傳遞?

    offset此時設定為-1,表示第一次複製

    主庫收到psync命令後,會用fullresync響應命令帶上2個引數:主庫的runid和主庫目前的複製進度offset,返回給從庫;

    從庫收到響應後,會記錄下這2個引數。這裡需要注意的是 fullresync表示的是第一次複製採用的全量複製

  2. 主庫將所有資料同步給從庫,從庫收到資料後,在本地完成資料載入,這個過程依賴於記憶體快照生成的RDB檔案。

    具體而言,主庫執行bgsave命令,生成RDB檔案,將檔案傳送給從庫。從庫接受到RDB檔案後,會先清空當前資料庫,然後載入RDB檔案。

    這裡為什麼要清空?

    是因為從庫在通過replicaof同步主庫之前,可能會存在資料,為了避免影響,先清空較好。

    那麼這個時候我們會產生疑問?在進行第一次全量同步時,同時寫入到主庫的資料不就沒有同步到從庫了嗎?

    為了保證資料的一致性,主庫在記憶體中會有專門的replication buffer,記錄rdb檔案生成後的所有寫操作。

  3. 這就來到了第三階段,主庫會將第二階段執行過程中,接受到的新命令,再發給從庫。

    具體的操作就是,當主庫完成RDB檔案傳送後,就會把此時的replication buffer中的修改操作發給從庫,從庫再重新執行這些操作。

    從而實現主從一致。

主從級聯模式分擔全量複製時的主庫壓力

那麼這個時候,也會遇到一個問題,我們可以發現在一次全量複製過程中,對於主庫來說,需要完成兩個耗時的工作;生成RDB檔案和傳輸RDB檔案。如果從庫數量很多,而且都要和主庫進行全量複製的話,就會導致主庫忙於fork子程式生成RDB檔案,進行資料同步。fork這個操作會阻塞主執行緒處理正常請求,從而導致主庫響應應用程式的請求變慢。此外,傳輸RDB檔案也會佔用主庫的網路頻寬,同樣會給主庫的資源使用帶來壓力。

那麼面臨這樣的問題,有什麼更好的解決方法呢?

“主-從-從”模式的產生,簡單來說,就是當我們在部署Redis叢集時,可以手動選一個從庫(內部配置資源較高的),用於其他從庫連線,建立主從關係。後續也會建立基於長連線的命令傳播,可以避免頻繁建立連線的開銷。

主從庫網路斷了怎麼辦?

採取增量複製的方式,Redis 存在 repl_backlog_buffer 這個緩衝區。

當主從庫斷連後,主庫會把斷連期間收到的寫操作命令寫入relication_buffer,同時也會將這些操作命令寫入repl_backlog_buffer這個緩衝區。

repl_backlog_buffer是一個環形緩衝區,主庫會記錄自己寫到的位置,從庫則會記錄自己已經讀到的位置,

master_repl_offset 是對應的偏移量。

主從庫的連線回復後,從庫首先會給主庫傳送psync命令,並把自己當前的slave_repl_offset發給主庫,主庫會判斷自己的

master_repl_offset; 在網路斷聯階段,主庫可能會收到新的寫操作命令,所以一般來說master_repl_offset會大於slave_repl_offset命令,

所以主庫只需要將2個之間的操作同步給從庫就可以。

增量複製的整體流程如下:

這裡需要注意的地方是,repl_backlog_buffer是一個環形緩衝區,所以在緩衝區寫滿之後,主庫會繼續寫入,就會覆蓋之前的操作記錄。

如果從庫的讀取速度比較慢,就有可能導致從庫還未讀取的操作被主庫新寫的覆蓋了,這樣會產生資料不一致。

並且如果slave_repl_offset位置上的資料被覆蓋掉了,從庫和主庫間將會進行全量複製。

這個問題我們可以如何避免呢?

關鍵引數 repl_backlog_size ,會影響到緩衝區空間大小。

緩衝空間大小 = 主庫的寫入速度 * 操作大小 - 主從庫網路傳輸命令的速度*操作大小。

通常情況下,考慮到一些突發的併發情況,我們會冗餘,所以repl_backlog_size = 緩衝空間大小 * 2.

舉個例子,如果主庫每秒寫入 2000 個操作,每個操作的大小為 2KB,網路每秒能傳輸 1000 個操作,那麼,有 1000 個操作需要緩衝起來,這就至少需要 2MB 的緩衝空間。否則,新寫的命令就會覆蓋掉舊操作了。為了應對可能的突發壓力,我們最終把 repl_backlog_size 設為 4MB。

總結

關於這塊理論知識比較多,需要反覆的閱讀檢視資料。理解之後,就會覺得也沒那麼複雜了。

最大的感觸就是,知道的越多,不知道的越多。

參考資料

極客時間《Redis核心技術與實戰》

本作品採用《CC 協議》,轉載必須註明作者和本文連結
不積跬步,無以至千里;不積小流,無以成江海

相關文章