這篇文章跟大家介紹一下Redis中主從架構資料一致性同步原理,希望對大家有幫助!
高可用有兩個意義:一是資料盡量不遺失,二是服務盡可能提供服務。 AOF 和 RDB 保證了資料持久化盡量不遺失,而主從複製就是增加副本,一份資料保存到多個實例上。即使有一個實例宕機,其他實例依然可以提供服務。
本篇主要帶大家全方位吃透 Redis 高可用技術解決方案之一主從複製架構。
本篇硬核,建議收藏慢慢品味,我相信讀者朋友會有一個質的提升。如有錯誤還望糾正,謝謝。關注“碼哥字節”設定“星標”第一時間接收優質文章,謝謝讀者的支持。
核心知識點
開篇寄語
問題 = 機會。遇到問題的時候,內心其實是開心的,越大的問題代表越大的機會。
任何事情都是有代價的,有得必有失,有失必有得,所以不必計較很多東西,我們只要想清楚自己要做什麼,並且想清楚自己願意為之付出什麼代價,然後就放手去做吧!
1. 主從複製概述
65 哥:有了RDB 和AOF 再也不怕宕機遺失資料了,但是Redis 實例宕機了怎麼實現高可用呢?
既然一台宕機了無法提供服務,那多台呢?是不是就可以解決了。 Redis 提供了主從模式,透過主從複製,將資料冗餘一份複製到其他 Redis 伺服器。
前者稱為主節點 (master),後者稱為從節點 (slave);資料的複製是單向的,只能由主節點到從節點。
預設情況下,每台 Redis 伺服器都是主節點;且一個主節點可以有多個從節點 (或沒有從節點),但一個從節點只能有一個主節點。
65 哥:主從之間的資料如何保證一致性呢?
為了確保副本資料的一致性,主從架構採用了讀寫分離的方式。
- 讀取操作:主、從庫都可以執行;
- 寫入操作:主庫先執行,之後將寫入操作同步到從庫;
#65 哥:為何要採用讀寫分離的方式?
我們可以假設主從庫都可以執行寫指令,假如對同一份資料分別修改了多次,每次修改發送到不同的主從實例上,就導致是實例的副本數據不一致了。
如果為了確保資料一致,Redis 需要加鎖,協調多個實例的修改,Redis 自然不會這麼做!
65 哥:主從複製還有其他作用麼?
- 故障復原:當主節點宕機,其他節點仍可提供服務;
- 負載平衡:Master 節點提供寫入服務,Slave 節點提供讀取服務,分擔壓力;
- 高可用基石:是哨兵和cluster 實施的基礎,是高可用的基石。
2. 搭建主從複製
主從複製的開啟,完全是在從節點發起的,不需要我們在主節點做任何事。
65 哥:怎麼搭建主從複製架構呀?
可以透過 replicaof(Redis 5.0 之前使用 slaveof)指令形成主函式庫和從函式庫的關係。
在從節點開啟主從複製,有3 種方式:
-
設定檔
在從伺服器的設定檔中加入
replicaof <masterip> <masterport></masterport></masterip>
-
#啟動指令
redis-server 啟動指令後面加入
--replicaof <masterip> < ;masterport></masterip>
-
客戶端指令
啟動多個Redis 實例後,直接透過客戶端執行指令:
replicaof <masterip> < ;masterport></masterip>
,則該Redis 實例成為從節點。
例如假設現在有實例1(172.16.88.1)、實例2(172.16.88.2)和實例3 (172.16.88.3),在實例2 和實例3 上分別執行下列指令,實例2 和實例3 就成為了實例1 的從函式庫,實例1 變成Master。
replicaof 172.16.88.1 6379复制代码
3. 主從複製原理
主從庫模式一旦採用了讀寫分離,所有資料的寫入操作只會在主庫上進行,不用協調三個實例。
主庫有了最新的資料後,會同步給從庫,這樣,主從庫的資料就是一致的。
65 哥:主從函式庫同步是如何完成的呢?主庫資料是一次性傳給從庫,還是分批同步?正常運作中又怎麼同步呢?要是主從庫間的網路斷連了,重新連接後資料還能保持一致嗎?
65 哥你問題咋這麼多,同步分為三種情況:
- 第一次主從庫全量複製;
- 主從正常運作期間的同步;
- 主從庫間網路斷開重連同步。
主從庫第一次全量複製
65 哥:我好暈啊,先從主從庫間第一次同步說起。
主從庫第一次複製過程大體可以分為3 個階段:連接建立階段(即準備階段)、主庫同步資料到從庫階段、發送同步期間新寫指令到從庫階段;
直接上圖,從整體上有一個全局觀的感知,後面具體介紹。
建立連接
該階段的主要功能是在主從節點之間建立連接,為資料全量同步做好準備。 從庫會和主庫建立連接,從庫執行 replicaof 並發送 psync 命令並告訴主庫即將進行同步,主庫確認回復後,主從庫間就開始同步了。
65 哥:從庫怎麼知道主庫資訊並建立連結的呢?
在從節點的設定檔中的 replicaof 設定項中配置了主節點的 IP 和 port 後,從節點就知道自己要和那個主節點連線了。
從節點內部維護了兩個字段,masterhost 和 masterport,用於儲存主節點的 IP 和 port 資訊。
從庫執行 replicaof
並發送 psync
命令,表示要執行資料同步,主庫收到命令後根據參數啟動複製。指令包含了主庫的 runID 和 複製進度 offset 兩個參數。
- runID:每個Redis 實例啟動都會自動產生一個唯一識別ID,第一次主從複製,還不知道主庫runID,參數設定為「?」 。
- offset:第一次複製設定為 -1,表示第一次複製,記錄複製進度偏移量。
主庫收到psync 指令後,會用FULLRESYNC 回應指令帶上兩個參數:主函式庫runID 和主函式庫目前的複製進度offset,回傳給從函式庫 。從庫收到回應後,會記錄下這兩個參數。
FULLRESYNC 回應表示第一次複製採用的全量複製,也就是說,主庫會把目前所有的資料複製給從函式庫。
主庫同步資料給從庫
第二階段
master 執行bgsave
命令產生RDB 文件,並將文件傳送給從庫,同時主函式庫為每一個slave 開闢一塊replication buffer 緩衝區記錄從產生RDB 檔案開始收到的所有寫入指令。
從庫收到 RDB 檔案後儲存到磁碟,並清空目前資料庫的數據,再載入 RDB 檔案資料到記憶體中。
傳送新寫入指令到從函式庫
第三階段
從節點載入RDB 完成後,主節點將replication buffer 緩衝區的資料傳送到從節點,Slave接收並執行,從節點同步至主節點相同的狀態。
65 哥:主庫將資料同步到從庫過程中,可以正常接受請求麼?
主庫不會被阻塞,Redis 身為唯快不破的男人,怎麼會動不動就阻塞呢。
在產生RDB 檔案之後的寫入操作並沒有記錄到剛剛的RDB 檔案中,為了確保主從庫資料的一致性,所以主庫會在記憶體中使用一個叫replication buffer 記錄RDB 檔案生成後的所有寫入操作。
65 哥:為啥從函式庫收到 RDB 檔案後要清空目前資料庫?
因為從庫在通過 replcaof
命令開始和主庫同步前可能保存了其他數據,防止主從數據之間的影響。
replication buffer 到底是什麼玩意?
一個在 master 端上建立的緩衝區,存放的資料是下面三個時間內所有的 master 資料寫入作業。
1)master 執行bgsave 產生RDB 的期間的寫入操作;
2)master 發送rdb 到slave 網路傳輸期間的寫入操作;
3)slave load rdb檔案把資料恢復到記憶體的期間的寫入操作。
Redis 和客戶端通信也好,和從庫通信也好,Redis 都分配一個內存buffer 進行數據交互,客戶端就是一個client,從庫也是一個client,我們每個client 連接上Redis後,Redis 都會分配一個專有client buffer,所有資料互動都是透過這個buffer 進行的。
Master 先把資料寫到這個 buffer 中,然後再透過網路傳送出去,這樣就完成了資料互動。
不管是主从在增量同步还是全量同步时,master 会为其分配一个 buffer ,只不过这个 buffer 专门用来传播写命令到从库,保证主从数据一致,我们通常把它叫做 replication buffer。
replication buffer 太小会引发的问题:
replication buffer 由 client-output-buffer-limit slave 设置,当这个值太小会导致主从复制连接断开。
1)当 master-slave 复制连接断开,master 会释放连接相关的数据。replication buffer 中的数据也就丢失了,此时主从之间重新开始复制过程。
2)还有个更严重的问题,主从复制连接断开,导致主从上出现重新执行 bgsave 和 rdb 重传操作无限循环。
当主节点数据量较大,或者主从节点之间网络延迟较大时,可能导致该缓冲区的大小超过了限制,此时主节点会断开与从节点之间的连接;
这种情况可能引起全量复制 -> replication buffer 溢出导致连接中断 -> 重连 -> 全量复制 -> replication buffer 缓冲区溢出导致连接中断……的循环。
具体详情:[top redis headaches for devops – replication buffer] 因而推荐把 replication buffer 的 hard/soft limit 设置成 512M。
config set client-output-buffer-limit "slave 536870912 536870912 0"复制代码
65 哥:主从库复制为何不使用 AOF 呢?相比 RDB 来说,丢失的数据更少。
这个问题问的好,原因如下:
RDB 文件是二进制文件,网络传输 RDB 和写入磁盘的 IO 效率都要比 AOF 高。
从库进行数据恢复的时候,RDB 的恢复效率也要高于 AOF。
增量复制
65 哥:主从库间的网络断了咋办?断开后要重新全量复制么?
在 Redis 2.8 之前,如果主从库在命令传播时出现了网络闪断,那么,从库就会和主库重新进行一次全量复制,开销非常大。
从 Redis 2.8 开始,网络断了之后,主从库会采用增量复制的方式继续同步。
增量复制:用于网络中断等情况后的复制,只将中断期间主节点执行的写命令发送给从节点,与全量复制相比更加高效。
repl_backlog_buffer
断开重连增量复制的实现奥秘就是 repl_backlog_buffer
缓冲区,不管在什么时候 master 都会将写指令操作记录在 repl_backlog_buffer
中,因为内存有限, repl_backlog_buffer
是一个定长的环形数组,如果数组内容满了,就会从头开始覆盖前面的内容。
master 使用 master_repl_offset
记录自己写到的位置偏移量,slave 则使用 slave_repl_offset
记录已经读取到的偏移量。
master 收到写操作,偏移量则会增加。从库持续执行同步的写指令后,在 repl_backlog_buffer
的已复制的偏移量 slave_repl_offset 也在不断增加。
正常情况下,这两个偏移量基本相等。在网络断连阶段,主库可能会收到新的写操作命令,所以 master_repl_offset
会大于 slave_repl_offset
。
当主从断开重连后,slave 会先发送 psync 命令给 master,同时将自己的 runID
,slave_repl_offset
发送给 master。
master 只需要把 master_repl_offset
与 slave_repl_offset
之间的命令同步给从库即可。
增量复制执行流程如下图:
65 哥:repl_backlog_buffer 太小的话从库还没读取到就被 Master 的新写操作覆盖了咋办?
我们要想办法避免这个情况,一旦被覆盖就会执行全量复制。我们可以调整 repl_backlog_size 这个参数用于控制缓冲区大小。计算公式:
repl_backlog_buffer = second * write_size_per_second复制代码
second:从服务器断开重连主服务器所需的平均时间;
write_size_per_second:master 平均每秒产生的命令数据量大小(写命令和数据大小总和);
例如,如果主服务器平均每秒产生 1 MB 的写数据,而从服务器断线之后平均要 5 秒才能重新连接上主服务器,那么复制积压缓冲区的大小就不能低于 5 MB。
为了安全起见,可以将复制积压缓冲区的大小设为2 * second * write_size_per_second
,这样可以保证绝大部分断线情况都能用部分重同步来处理。
基于长连接的命令传播
65 哥:完成全量同步后,正常运行过程如何同步呢?
当主从库完成了全量复制,它们之间就会一直维护一个网络连接,主库会通过这个连接将后续陆续收到的命令操作再同步给从库,这个过程也称为基于长连接的命令传播,使用长连接的目的就是避免频繁建立连接导致的开销。
在命令传播阶段,除了发送写命令,主从节点还维持着心跳机制:PING 和 REPLCONF ACK。
主->从:PING
每隔指定的时间,主节点会向从节点发送 PING 命令,这个 PING 命令的作用,主要是为了让从节点进行超时判断。
从->主:REPLCONF ACK
在命令传播阶段,从服务器默认会以每秒一次的频率,向主服务器发送命令:
REPLCONF ACK <replication_offset>复制代码</replication_offset>
其中 replication_offset 是从服务器当前的复制偏移量。发送 REPLCONF ACK 命令对于主从服务器有三个作用:
检测主从服务器的网络连接状态。
辅助实现 min-slaves 选项。
检测命令丢失, 从节点发送了自身的 slave_replication_offset,主节点会用自己的 master_replication_offset 对比,如果从节点数据缺失,主节点会从
repl_backlog_buffer
缓冲区中找到并推送缺失的数据。注意,offset 和 repl_backlog_buffer 缓冲区,不仅可以用于部分复制,也可以用于处理命令丢失等情形;区别在于前者是在断线重连后进行的,而后者是在主从节点没有断线的情况下进行的。
如何确定执行全量同步还是部分同步?
在 Redis 2.8 及以后,从节点可以发送 psync 命令请求同步数据,此时根据主从节点当前状态的不同,同步方式可能是全量复制或部分复制。本文以 Redis 2.8 及之后的版本为例。
关键就是 psync
的执行:
-
从节点根据当前状态,发送
psync
命令给 master:- 如果从节点从未执行过
replicaof
,则从节点发送psync ? -1
,向主节点发送全量复制请求; - 如果从节点之前执行过
replicaof
则发送psync <runid> <offset></offset></runid>
, runID 是上次复制保存的主节点 runID,offset 是上次复制截至时从节点保存的复制偏移量。
- 如果从节点从未执行过
-
主节点根据接受到的
psync
命令和当前服务器状态,决定执行全量复制还是部分复制:- runID 与从节点发送的 runID 相同,且从节点发送的
slave_repl_offset
之后的数据在repl_backlog_buffer
缓冲区中都存在,则回复CONTINUE
,表示将进行部分复制,从节点等待主节点发送其缺少的数据即可; - runID 与从节点发送的 runID 不同,或者从节点发送的 slave_repl_offset 之后的数据已不在主节点的
repl_backlog_buffer
缓冲区中 (在队列中被挤出了),则回复从节点FULLRESYNC <runid> <offset></offset></runid>
,表示要进行全量复制,其中 runID 表示主节点当前的 runID,offset 表示主节点当前的 offset,从节点保存这两个值,以备使用。
- runID 与从节点发送的 runID 相同,且从节点发送的
一个从库如果和主库断连时间过长,造成它在主库 repl_backlog_buffer
的 slave_repl_offset 位置上的数据已经被覆盖掉了,此时从库和主库间将进行全量复制。
总结下
每个从库会记录自己的 slave_repl_offset
,每个从库的复制进度也不一定相同。
在和主库重连进行恢复时,从库会通过 psync 命令把自己记录的 slave_repl_offset
发给主库,主库会根据从库各自的复制进度,来决定这个从库可以进行增量复制,还是全量复制。
replication buffer 和 repl_backlog
replication buffer 對應於每個 slave,透過
config set client-output-buffer-limit slave
設定。repl_backlog_buffer
是一個環形緩衝區,整個 master 行程只會存在一個,所有的 slave 公用。 repl_backlog 的大小透過repl-backlog-size 參數設置,預設大小是1M,其大小可以根據每秒產生的命令、(master 執行rdb bgsave) ( master 發送rdb 到slave) (slave load rdb 檔案)時間之和來估算積壓緩衝區的大小,repl-backlog-size 值不小於這兩者的乘積。
總的來說,replication buffer
是主從庫在進行全量複製時,主庫上用於和從庫連接的客戶端的buffer,而repl_backlog_buffer
是為了支援從函式庫增量複製,主函式庫上用來持續保存寫入作業的一塊專用buffer。
repl_backlog_buffer
是一塊專用 buffer,在 Redis 伺服器啟動後,開始一直接收寫入操作命令,這是所有從程式庫共享的。主庫和從庫會各自記錄自己的複製進度,所以,不同的從庫在進行恢復時,會把自己的複製進度(slave_repl_offset
)發給主庫,主庫就可以和它獨立同步。
如圖所示:
4. 主從應用問題
4.1 讀寫分離的問題
資料過期問題
65 哥:主從複製的場景下,從節點會刪除過期資料麼?
這個問題問得好,為了主從節點的資料一致性,從節點不會主動刪除資料。我們知道 Redis 有兩種刪除策略:
惰性刪除:當客戶端查詢對應的資料時,Redis 判斷資料是否過期,過期則刪除。
定期刪除:Redis 透過定時任務刪除過期資料。
65 哥:那客戶端透過從節點讀取資料會不會讀取到過期資料?
Redis 3.2 開始,透過從節點讀取資料時,先判斷資料是否已過期。如果過期則不傳回客戶端,並且刪除資料。
4.2 單機記憶體大小限制
如果 Redis 單機記憶體達到 10GB,一個從節點的同步時間在幾分鐘的層級;如果從節點較多,恢復的速度會更慢。如果系統的讀取負載很高,而這段時間從節點無法提供服務,會對系統造成很大的壓力。
如果資料量過大,全量複製階段主節點fork 保存RDB 檔案耗時過大,從節點長時間接收不到資料觸發逾時,主從節點的資料同步同樣可能陷入全量複製->超時導致複製中斷->重連->全量複製->超時導致複製中斷…的循環。
此外,主節點單機記憶體除了絕對量不能太大,其佔用主機內存的比例也不應過大:最好只使用50% - 65% 的內存,留下30%-45%的記憶體用於執行bgsave 命令和建立複製緩衝區等。
總結
主從複製的作用:AOF 和 RDB 二進位檔案保證了宕機快速恢復數據,盡可能的防止遺失資料。但宕機後依然無法提供服務,所以便演化出主從架構、讀寫分離。
主從複製原理:連線建立階段、資料同步階段、指令傳播階段;資料同步階段又分為全量複製與部分複製;指令傳播階段主從節點之間有PING 和REPLCONF ACK 指令互相進行心跳檢測。
主從複製雖然解決或緩解了資料冗餘、故障復原、讀取負載平衡等問題,但其缺陷仍很明顯:故障復原無法自動化;寫入操作無法負載平衡;儲存能力受到單機的限制;這些問題的解決,需要哨兵和叢集的幫助,我將在後面的文章中介紹,歡迎關注。
原文網址:https://juejin.cn/post/6973928120332058654
作者:碼哥位元組
#更多程式相關知識,請造訪:程式設計影片! !
以上是深入了解Redis中主從架構資料一致性同步原理的詳細內容。更多資訊請關注PHP中文網其他相關文章!

Redis的關鍵特性包括速度、靈活性和豐富的數據結構支持。 1)速度:Redis作為內存數據庫,讀寫操作幾乎瞬時,適用於緩存和會話管理。 2)靈活性:支持多種數據結構,如字符串、列表、集合等,適用於復雜數據處理。 3)數據結構支持:提供字符串、列表、集合、哈希表等,適合不同業務需求。

Redis的核心功能是高性能的內存數據存儲和處理系統。 1)高速數據訪問:Redis將數據存儲在內存中,提供微秒級別的讀寫速度。 2)豐富的數據結構:支持字符串、列表、集合等,適應多種應用場景。 3)持久化:通過RDB和AOF方式將數據持久化到磁盤。 4)發布訂閱:可用於消息隊列或實時通信系統。

Redis支持多種數據結構,具體包括:1.字符串(String),適合存儲單一值數據;2.列表(List),適用於隊列和棧;3.集合(Set),用於存儲不重複數據;4.有序集合(SortedSet),適用於排行榜和優先級隊列;5.哈希表(Hash),適合存儲對像或結構化數據。

Redis計數器是一種使用Redis鍵值對存儲來實現計數操作的機制,包含以下步驟:創建計數器鍵、增加計數、減少計數、重置計數和獲取計數。 Redis計數器的優勢包括速度快、高並發、持久性和簡單易用。它可用於用戶訪問計數、實時指標跟踪、遊戲分數和排名以及訂單處理計數等場景。

使用 Redis 命令行工具 (redis-cli) 可通過以下步驟管理和操作 Redis:連接到服務器,指定地址和端口。使用命令名稱和參數向服務器發送命令。使用 HELP 命令查看特定命令的幫助信息。使用 QUIT 命令退出命令行工具。

Redis集群模式通過分片將Redis實例部署到多個服務器,提高可擴展性和可用性。搭建步驟如下:創建奇數個Redis實例,端口不同;創建3個sentinel實例,監控Redis實例並進行故障轉移;配置sentinel配置文件,添加監控Redis實例信息和故障轉移設置;配置Redis實例配置文件,啟用集群模式並指定集群信息文件路徑;創建nodes.conf文件,包含各Redis實例的信息;啟動集群,執行create命令創建集群並指定副本數量;登錄集群執行CLUSTER INFO命令驗證集群狀態;使

要從 Redis 讀取隊列,需要獲取隊列名稱、使用 LPOP 命令讀取元素,並處理空隊列。具體步驟如下:獲取隊列名稱:以 "queue:" 前綴命名,如 "queue:my-queue"。使用 LPOP 命令:從隊列頭部彈出元素並返回其值,如 LPOP queue:my-queue。處理空隊列:如果隊列為空,LPOP 返回 nil,可先檢查隊列是否存在再讀取元素。

Redis 集群中使用 zset:zset 是一種有序集合,將元素與評分關聯。分片策略: a. 哈希分片:根據 zset 鍵的哈希值分佈。 b. 範圍分片:根據元素評分劃分為範圍,並將每個範圍分配給不同的節點。讀寫操作: a. 讀操作:如果 zset 鍵屬於當前節點的分片,則在本地處理;否則,路由到相應的分片。 b. 寫入操作:始終路由到持有 zset 鍵的分片。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

MinGW - Minimalist GNU for Windows
這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

WebStorm Mac版
好用的JavaScript開發工具

SecLists
SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

Dreamweaver Mac版
視覺化網頁開發工具

Safe Exam Browser
Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。