概述
一般來說,要將Redis運用於工程項目中,只使用一臺Redis是萬萬不能的,原因如下:
- 從結構上,單個Redis服務器會發生單點故障,並且一臺服務器需要處理所有的請求負載,壓力較大;
- 從容量上,單個Redis服務器內存容量有限,就算一臺Redis服務器內容容量爲256G,也不能將所有內容用作Redis存儲內存,一般來說,單臺Redis最大使用內存不應該超過20G。
本文先討論第一點的解決方案:Redis主從複製,第二點可以使用Redis集羣解決,下一篇文章將介紹Redis集羣。
主從複製
考慮如下一種場景:
電子商務網站上的商品,一般都是一次上傳,無數次瀏覽的,說專業點也就是”多讀少寫”。
對於這種場景,我們可以使如下這種架構:
如圖中所示,我們將一臺Redis服務器作主庫(Matser),其他三臺作爲從庫(Slave),主庫只負責寫數據,每次有數據更新都將更新的數據同步到它所有的從庫,而從庫只負責讀數據。這樣一來,就有了兩個好處:
- 讀寫分離,不僅可以提高服務器的負載能力,並且可以根據讀請求的規模自由增加或者減少從庫的數量,棒極了;
- 數據被複製成了了好幾份,就算有一臺機器出現故障,也可以使用其他機器的數據快速恢復。
需要注意的是:在Redis主從模式中,一臺主庫可以擁有多個從庫,但是一個從庫只能隸屬於一個主庫。
配置
在Redis中,要實現主從複製架構非常簡單,只需要在從數據庫的配置文件中加上如下命令即可:
1
|
slaveof 主數據庫地址 主數據庫端口
|
主數據庫不需要任何配置。
示例
下面將演示怎麼實現一個簡單的複製系統。我們在一臺機器上起兩個Redis實例,監聽不同的端口,其中一個作爲主庫,另外一個作爲從庫。首先不加任何參數來啓動一個Redis實例作爲主數據庫:
可以看到,主庫監聽的是6379端口。
然後加上slaveof參數啓動另一個Redis實例作爲從庫,並且監聽6380端口:
從控制檯輸出中可以看到,從庫已經連接到主庫:126.0.0.1:6379了,看樣子主從複製系統配置成功。我們可以分別在主庫和從庫中使用如下命令看一看當前實例在複製系統中的相關信息:
接下來驗證一把。
首先在主庫中設置一個鍵值:
1 2 3 4 |
[qifuguang@Mac~]$ /opt/soft/redis-3.0.4/src/redis-cli -p 6379 127.0.0.1:6379> set test-sync winwill2012 OK 127.0.0.1:6379> |
現在到從庫中檢查該值是否已經自動同步到了從庫:
1 2 3 4 |
[qifuguang@Mac~]$ /opt/soft/redis-3.0.4/src/redis-cli -p 6380 127.0.0.1:6380> get test-sync "winwill2012" 127.0.0.1:6380> |
可以看到,數據確實從主庫同步到了從庫.
在默認情況下,從庫是隻讀的,如果在從庫中寫數據將會報錯:
1 2 3 4 |
[qifuguang@Mac~]$ /opt/soft/redis-3.0.4/src/redis-cli -p 6380 127.0.0.1:6380> set x y (error) READONLY You can't write against a read only slave. 127.0.0.1:6380> |
但是可以在從庫的配置文件中加上如下的配置項允許從庫寫數據:
1
|
slave-read-only no
|
但是,因爲從庫中修改的數據不會被同步到任何其他數據庫,並且一旦主庫修改了數據,從庫的數據就會因爲自動同步被覆蓋,所以一般情況下,不建議將從庫設置爲可寫。
相同的道理,配置多臺從庫也使用相同的方法,都在從庫的配置文件中加上slaveof參數即可。
此外,我們可以在客戶端使用命令
1
|
SLAVEOF 新主庫地址 新主庫端口
|
來修改當前數據庫的主庫,如果當前數據庫已經是其他庫的從庫, 則當前數據庫會停止和原來的數據庫的同步而和新的數據庫同步。
最後,從數據庫還可以通過運行命令:
1
|
SLAVEOF NO ONE
|
來停止接受來自其他數據庫的同步而升級成爲主庫。
原理
上面說了配置主從複製系統的方法,並且舉例例子詳細說明,本節將介紹Redis主從複製的實現原理。
當一個從數據庫啓動時,會向主數據庫發送SYNC命令,主數據庫收到命令後會開始在後臺保存快照(即RDB持久化過程),並將保存快照期間接收到的命令緩存起來。當快照完成後,Redis會將快照文件和緩存的命令發給從數據庫,從數據庫收到數據後,會載入快照文件並執行緩存的命令。以上過程稱爲複製初始化。複製初始化之結束後,主數據庫每收到寫命令時就會將命令同步給從數據庫,從而保證主從數據庫數據一致,這一過程稱爲複製同步階段。
有兩點需要注意:
- 當主從數據庫之間的連接斷開後,Redis2.8之前的版本會重新進行復制初始化過程,這樣就使得主從數據庫斷開連接後數據恢復的過程的效率很低下。Redis2.8版本的一個重要改進就是斷線支持有條件的增量數據傳輸,當從數據庫再次連接到主數據庫時,主數據庫只需要將斷線期間執行的命令發給從數據庫即可,大大提高了Redis主從複製的實用性。
- 複製同步階段貫穿整個主從同步過程的始終,直到主從關係終止爲止。在複製過程中,即使關閉了RDB方式的持久化(刪除所有save參數),依舊會執行快照操作。
樂觀複製
Redis採用了複製的策略。容忍在一定時間內主從數據庫的內容是不同的,但是兩者的數據最終會保持一致。具體來說,Redis主從數據庫之間的複製數據的過程本身是異步的,這意味着,主數據庫執行完客戶端的寫請求後會立即將命令在主數據庫的執行結果返回給客戶端,而不會等待從數據庫收到該命令後再返回給客戶端。這一特性保證了複製後主從數據庫的性能不會受到影響,但另一方面也會產生一個主從數據庫數據不一致的時間窗口,當主數據庫執行一條寫命令之後,主數據庫的數據已經發生變動,然而在主數據庫將該命令傳送給從數據庫之前,如果兩個數據庫之間的連接斷開了,此時二者間的數據就不一致了。從這個角度看,主數據庫無法得知命令最終同步給了幾個從數據庫,不過Redis提供了兩個配置選項來限制只有至少同步給指定數量的數據庫時,主數據庫纔是可寫的:
1 2 |
min-slaves-to-write 3 min-slave2-max-lag 10 |
第一個參數表示只有當3個或3個以上的從數據庫連接到主庫時,主數據庫纔是可寫的,否則返回錯誤。
第二個參數表示允許從數據庫失去連接的最長時間,該選項默認是關閉的,在分佈式系統中,打開併合理配置該選項可以降低主從架構因爲網絡分區導致的數據不一致問題。
圖結構
從數據庫不僅可以接收主數據庫的數據,同時也可以作爲主數據庫存在,形成類似圖的結構,如下圖:
A中的數據會同步到B,C中,C中的數據會同步到D,E中。