05丨数据同步:主从库如何实现数据一致
文章目錄
- 1. 主從庫數據同步介紹
- 2. 主從庫間如何進行第一次同步
- 3.主從級聯模式分擔全量復制時的主庫壓力
- 4.主從庫間網絡斷了怎么辦?
1. 主從庫數據同步介紹
??Redis 具有高可靠性有兩層含義:一是數據盡量少丟失,二是服務盡量少中斷。AOF 和 RDB 保證了前者,而對于后者,Redis 的做法就是增加副本冗余量,將一份數據同時保存在多個實例上。即使有一個實例出現了故障,需要過一段時間才能恢復,其他實例也可以對外提供服務,不會影響業務使用。
??實際上,Redis 提供了主從庫模式,以保證數據副本的一致,主從庫之間采用的是讀寫分離的方式。
- 讀操作:主庫、從庫都可以接收;
- 寫操作:首先到主庫執行,然后,主庫將寫操作同步給從庫。
??你可以設想一下,如果在上圖中,不管是主庫還是從庫,都能接收客戶端的寫操作,那么,一個直接的問題就是:如果客戶端對同一個數據(例如 k1)前后修改了三次,每一次的修改請求都發送到不同的實例上,在不同的實例上執行,那么,這個數據在這三個實例
2. 主從庫間如何進行第一次同步
??當我們啟動多個 Redis 實例的時候,它們相互之間就可以通過 replicaof(Redis 5.0 之前使用 slaveof)命令形成主庫和從庫的關系,之后會按照三個階段完成數據的第一次同步。
??例如,現在有實例 1(ip:192.168.0.2)和實例 2(ip:192.168.0.3),我們在實例 2 上執行以下這個命令后,實例 2 就變成了實例 1 的從庫,并從實例 1 上復制數據:
??接下來,我們就要學習主從庫間數據第一次同步的三個階段了。你可以先看一下下面這張圖,有個整體感知,接下來我再具體介紹。
??第一階段是主從庫間建立連接、協商同步的過程,主要是為全量復制做準備。在這一步,從庫和主庫建立起連接,并告訴主庫即將進行同步,主庫確認回復后,主從庫間就可以開始同步了。
??具體來說,從庫給主庫發送 psync 命令,表示要進行數據同步,主庫根據這個命令的參數來啟動復制。psync 命令包含了主庫的 runID和復制進度 offset兩個參數。
- runID,是每個 Redis 實例啟動時都會自動生成的一個隨機 ID,用來唯一標記這個實例。當從庫和主庫第一次復制時,因為不知道主庫的 runID,所以將 runID 設為“?”。
- offset,此時設為 -1,表示第一次復制。
??主庫收到 psync 命令后,會用 FULLRESYNC 響應命令帶上兩個參數:主庫 runID 和主庫目前的復制進度 offset,返回給從庫。從庫收到響應后,會記錄下這兩個參數。
??這里有個地方需要注意,FULLRESYNC 響應表示第一次復制采用的全量復制,也就是說,主庫會把當前所有的數據都復制給從庫。
??在第二階段,主庫將所有數據同步給從庫。從庫收到數據后,在本地完成數據加載。這個過程依賴于內存快照生成的 RDB 文件。
??具體來說,主庫執行 bgsave 命令,生成 RDB 文件,接著將文件發給從庫。從庫接收到RDB 文件后,會先清空當前數據庫,然后加載 RDB 文件。這是因為從庫在通過 replicaof命令開始和主庫同步前,可能保存了其他數據。為了避免之前數據的影響,從庫需要先把當前數據庫清空。
??在主庫將數據同步給從庫的過程中,主庫不會被阻塞,仍然可以正常接收請求。否則,Redis 的服務就被中斷了。但是,這些請求中的寫操作并沒有記錄到剛剛生成的 RDB 文件中。為了保證主從庫的數據一致性,主庫會在內存中用專門的 replication buffer,記錄RDB 文件生成后收到的所有寫操作。
??最后,也就是第三個階段,主庫會把第二階段執行過程中新收到的寫命令,再發送給從庫。具體的操作是,當主庫完成 RDB 文件發送后,就會把此時 replication buffer 中的修改操作發給從庫,從庫再重新執行這些操作。這樣一來,主從庫就實現同步了。
3.主從級聯模式分擔全量復制時的主庫壓力
??通過分析主從庫間第一次數據同步的過程,你可以看到,一次全量復制中,對于主庫來說,需要完成兩個耗時的操作:生成 RDB 文件和傳輸 RDB 文件。
??如果從庫數量很多,而且都要和主庫進行全量復制的話,就會導致主庫忙于 fork 子進程生成 RDB 文件,進行數據全量同步。fork 這個操作會阻塞主線程處理正常請求,從而導致主庫響應應用程序的請求速度變慢。此外,傳輸 RDB 文件也會占用主庫的網絡帶寬,同樣會給主庫的資源使用帶來壓力。那么,有沒有好的解決方法可以分擔主庫壓力呢?
??其實是有的,這就是“主 - 從 - 從”模式。
??在剛才介紹的主從庫模式中,所有的從庫都是和主庫連接,所有的全量復制也都是和主庫進行的。現在,我們可以通過“主 - 從 - 從”模式將主庫生成 RDB 和傳輸 RDB 的壓力,以級聯的方式分散到從庫上。簡單來說,我們在部署主從集群的時候,可以手動選擇一個從庫(比如選擇內存資源配置較高的從庫),用于級聯其他的從庫。然后,我們可以再選擇一些從庫(例如三分之一的從庫),在這些從庫上執行如下命令,讓它們和剛才所選的從庫,建立起主從關系。
??這樣一來,這些從庫就會知道,在進行同步時,不用再和主庫進行交互了,只要和級聯的從庫進行寫操作同步就行了,這就可以減輕主庫上的壓力,如下圖所示:
??好了,到這里,我們了解了主從庫間通過全量復制實現數據同步的過程,以及通過“主 -從 - 從”模式分擔主庫壓力的方式。那么,一旦主從庫完成了全量復制,它們之間就會一直維護一個網絡連接,主庫會通過這個連接將后續陸續收到的命令操作再同步給從庫,這個過程也稱為基于長連接的命令傳播,可以避免頻繁建立連接的開銷。
??聽上去好像很簡單,但不可忽視的是,這個過程中存在著風險點,最常見的就是網絡斷連或阻塞。如果網絡斷連,主從庫之間就無法進行命令傳播了,從庫的數據自然也就沒辦法和主庫保持一致了,客戶端就可能從從庫讀到舊數據。
4.主從庫間網絡斷了怎么辦?
??從 Redis 2.8 開始,網絡斷了之后,主從庫會采用增量復制的方式繼續同步。聽名字大概就可以猜到它和全量復制的不同:全量復制是同步所有數據,而增量復制只會把主從庫網絡斷連期間主庫收到的命令,同步給從庫。
??當主從庫斷連后,主庫會把斷連期間收到的寫操作命令,寫入 replication buffer,同時也會把這些操作命令也寫入 repl_backlog_buffer 這個緩沖區。repl_backlog_buffer 是一個環形緩沖區,主庫會記錄自己寫到的位置,從庫則會記錄自己已經讀到的位置。
??剛開始的時候,主庫和從庫的寫讀位置在一起,這算是它們的起始位置。隨著主庫不斷接收新的寫操作,它在緩沖區中的寫位置會逐步偏離起始位置,我們通常用偏移量來衡量這個偏移距離的大小,對主庫來說,對應的偏移量就是 master_repl_offset。主庫接收的新寫操作越多,這個值就會越大。
??同樣,從庫在復制完寫操作命令后,它在緩沖區中的讀位置也開始逐步偏移剛才的起始位置,此時,從庫已復制的偏移量 slave_repl_offset 也在不斷增加。正常情況下,這兩個偏移量基本相等。
??主從庫的連接恢復之后,從庫首先會給主庫發送 psync 命令,并把自己當前的slave_repl_offset 發給主庫,主庫會判斷自己的 master_repl_offset 和 slave_repl_offset之間的差距。
??在網絡斷連階段,主庫可能會收到新的寫操作命令,所以,一般來說,master_repl_offset會大于 slave_repl_offset。此時,主庫只用把 master_repl_offset 和 slave_repl_offset之間的命令操作同步給從庫就行。
??就像剛剛示意圖的中間部分,主庫和從庫之間相差了 put d e 和 put d f 兩個操作,在增量復制時,主庫只需要把它們同步給從庫,就行了。說到這里,我們再借助一張圖,回顧下增量復制的流程。
??不過,有一個地方我要強調一下,因為 repl_backlog_buffer 是一個環形緩沖區,所以在緩沖區寫滿后,主庫會繼續寫入,此時,就會覆蓋掉之前寫入的操作。如果從庫的讀取速度比較慢,就有可能導致從庫還未讀取的操作被主庫新寫的操作覆蓋了,這會導致主從庫間的數據不一致。
??因此,我們要想辦法避免這一情況,一般而言,我們可以調整repl_backlog_size這個參數。這個參數和所需的緩沖空間大小有關。緩沖空間的計算公式是:緩沖空間大小 = 主庫寫入命令速度 * 操作大小 - 主從庫間網絡傳輸命令速度 * 操作大小。在實際應用中,考慮到可能存在一些突發的請求壓力,我們通常需要把這個緩沖空間擴大一倍,即repl_backlog_size = 緩沖空間大小 * 2,這也就是 repl_backlog_size 的最終值。
??舉個例子,如果主庫每秒寫入 2000 個操作,每個操作的大小為 2KB,網絡每秒能傳輸1000 個操作,那么,有 1000 個操作需要緩沖起來,這就至少需要 2MB 的緩沖空間。否則,新寫的命令就會覆蓋掉舊操作了。為了應對可能的突發壓力,我們最終把repl_backlog_size 設為 4MB。
這樣一來,增量復制時主從庫的數據不一致風險就降低了。
??不過,如果并發請求量非常大,連兩倍的緩沖空間都存不下新操作請求的話,此時,主從庫數據仍然可能不一致。
??針對這種情況,一方面,你可以根據 Redis 所在服務器的內存資源再適當增加repl_backlog_size 值,比如說設置成緩沖空間大小的 4 倍,另一方面,你可以考慮使用切片集群來分擔單個主庫的請求壓力。
總結
以上是生活随笔為你收集整理的05丨数据同步:主从库如何实现数据一致的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: lombok中的@Data注解与MyBa
- 下一篇: 06 | 哨兵机制: 主库挂了, 如何不