標籤:
一、主從複製架構簡介
通過前面幾篇的介紹中,我們都是在單機上使用Redis進行相關的實踐操作,從本篇起,我們將初步探索一下Redis的叢集,而叢集中最經典的架構便是主從複製架構。那麼,我們首先來瞭解一下神馬是主從複製架構?
1.1 源於關聯式資料庫的讀寫分離
隨著網站業務的不斷髮展,使用者量的不斷增加,資料量也成倍的增長,資料庫的訪問量也呈線性地增長。特別是在使用者訪問高峰期間,並發訪問量突然增大,資料庫的負載壓力也會增大,如果架構方案不夠健壯,那麼資料庫伺服器很有可能在高並發訪問負載壓力下宕機,造成資料訪問服務的失效,從而導致網站的業務中斷,給公司和使用者造成雙重損失。那麼,有木有一種方案能夠解決此問題,使得資料庫不再因為負載壓力過高而成為網站的瓶頸呢?答案肯定是有的。
目前,大部分的主流關係型資料庫都提供了主從熱備功能,通過配置兩台(或多台)資料庫的主從關係,可以將一台資料庫伺服器的資料更新同步到另一台伺服器上。網站可以利用資料庫的這一功能,實現資料庫的讀寫分離,從而改善資料庫的負載壓力。
利用資料庫的讀寫分離,Web伺服器在寫資料的時候,訪問主要資料庫(Master),主要資料庫通過主從複製機制將資料更新同步到從資料庫(Slave),這樣當Web伺服器讀資料的時候,就可以通過從資料庫獲得資料。這一方案使得在大量讀操作的Web應用可以輕鬆地讀取資料,而主要資料庫也只會承受少量的寫入操作,還可以實現資料熱備份,可謂是一舉兩得的方案。
1.2 基於MySQL的資料複製流程
剛剛我們瞭解了關係型資料庫的讀寫分離能夠實現資料庫的主從架構,那麼主從架構中最重要的資料複製又是怎麼一回事呢?MySQL作為最流行的關係型資料庫之一,通過瞭解MySQL的資料複製流程,會使得我們對Redis主從複製的認知會有一定的協助。
從來看,整體上有如下三個步湊:
(1)Master將改變記錄到二進位日誌(binary log)中(這些記錄叫做二進位日誌事件,binary log events);
(2)Slave將Master的二進位日誌事件(binary log events)拷貝到它的中繼日誌(relay log);
PS:可以看出,Slave伺服器中有一個I/O線程(I/O Thread)在不停地監聽Master的二進位日誌(Binary Log)是否有更新:如果沒有它會睡眠等待Master產生新的日誌事件;如果有新的日誌事件(Log Events),則會將其拷貝至Slave伺服器中的中繼日誌(Relay Log)。
(3)Slave重做中繼日誌(Relay Log)中的事件,將Master上的改變反映到它自己的資料庫中。
PS:可以看出,Slave伺服器中有一個SQL線程(SQL Thread)從中繼日誌讀取事件,並重做其中的事件從而更新Slave的資料,使其與Master中的資料一致。只要該線程與I/O線程保持一致,中繼日誌通常會位於OS的緩衝中,所以中繼日誌的開銷很小。
經過了上面的簡單簡介,我們初步瞭解了什麼是主從複製,以及在關係型資料庫中資料是如何複製的。在這,我們不由疑問在Redis中又是怎樣實現資料複製的呢?別急,我們先來實踐一下,先對主從複製得到一個感性認識,再由感性認識升到理性認識去理解一下。So,Let‘s start doing.
二、在單機上類比主從複製架構實踐2.1 拷貝兩個服務到指定磁碟檔案夾
(1)將第一篇中下載的Redis服務檔案夾複製兩份,並給兩個檔案夾取名為:RedisMasterService與RedisSlaveService;
(2)將兩個檔案夾拷貝到Windows中指定的檔案夾中,我這裡統一拷貝到E:\下,便於後面的啟動測試;
2.2 分別修改Master和Slave的設定檔
(1)修改Master服務的設定檔(redis.conf)
這裡,主要修改一下Master服務所綁定的IP地址(即Master伺服器的IP地址),我這裡由於是在本機進行的,所以直接設定為127.0.0.1即可。通過在redis.conf中搜尋"bind",可以看到預設配置已經有了#bind 127.0.0.1的字串,我們要做的只是將這句取消注釋就可以了。
PS:建議使用類似於EditPlus、Notepad++等專業一點的編輯器開啟redis.conf設定檔,這樣尋找和編輯都比較直觀明了。如果這些你都沒有,那你可以用Visual Studio開啟來編輯(如果你連VS也沒有,我只能呵呵了,你用記事本編輯吧,麼麼嗒)。好吧,我就沒有EditPlus和Notepad++,重裝了系統就沒有裝這些編輯器,被你們看穿了。
(2)修改Slave服務的設定檔(redis.conf)
①修改Slave綁定的連接埠號碼:這裡因為Master和Slave都在一台機器上,因此需要修改連接埠號碼以區分兩個Redis服務。如果不修改,則預設連接埠號碼位6379;修改方法也很簡單,搜尋"port"關鍵詞,將port 6379改為port 6380即可。注意,這裡只要不為6379即可,你可以隨便改,6380隻是我這裡設定的連接埠號碼而已。
②修改Slave綁定的IP地址,這裡和Master一致,都改為bind 127.0.0.1即可;
③修改Master與Slave的對應關係配置:搜尋"slaveof"關鍵詞,會找到這樣一句:# slaveof <masterip> <masterport>。我們要做的就是,取消這句話的注釋,並將<masterip>和<masterport>改為主伺服器的IP地址和連接埠號碼,這裡我們的Master服務還是本機,因此改為slaveof 127.0.0.1 6379即可。
2.3 分別啟動Master和Slave的服務
(1)啟動Master的服務:通過cmd跳轉到Master檔案夾下,使用redis-server.exe redis.conf命令啟動Master服務,這裡需要指定redis.conf是因為我們剛剛編輯修改了redis.conf,需要重新載入設定檔;
(2)啟動Slave的服務:操作步湊同上,也是redis-server.exe redis.conf,注意別忘了加上redis.conf這句;
(3)Slave服務啟動完成後,我們可以看到cmd中出現了一些不一樣的日誌資訊。這裡,我們來簡單的瞭解一下:
①首先,在Slave服務的命令列中出現如下的日誌資訊:
這裡,可以看到通過編輯了設定檔後,Slave服務在啟動後便會主動地串連Master服務(這裡主要是通過發送SYNC命令請求同步串連),中間Master會發一個PING命令來檢測Slave的存活狀態(存在則繼續複製,失效則終止後面步湊),然後等待Master發送回其記憶體快照檔案(這裡你先將這個記憶體快照檔案理解為一個資料備份檔案或日誌)。可以看出,這裡Slave已經接受了Master的36 bytes資料,並將資料存放區到了記憶體中,最終成功完成與Master的同步。
②其次,在Master服務的命令列中出現了如下的日誌資訊:
這裡,Master通過檢測發現有Slave發送了SYNC命令來判斷Master中是否有記憶體快照(或者更新的記憶體快照),沒有則開始執行記憶體快照(主要是將),有則等待其結束然後將快照檔案發送給Slave。至此,Master端就終止了此次的SYNC通訊。而Slave則會將資料快照檔案儲存到本地,待接收完成後,清空記憶體表,重新讀取Master發來的記憶體快照檔案,形成一個狀態的迴圈。
2.4 在命令列中進行簡單資料讀寫測試
PS:這裡主要是通過新開兩個cmd視窗來類比兩個用戶端來操作Redis
(1)在Master服務中寫入一個Key/Value資料對
①首先,新開一個cmd,並在cmd中跳轉到Master檔案夾下,使用redis-cli.exe -p 6379命令進入MasterRedis用戶端命令模式
②然後,為了確保測試成功,我們先將當前的Master中的資料清空一下,使用flushdb命令清空已存的所有資料(Redis會定期將資料從記憶體寫入檔案中實現資料的持久化)
③最後,通過一個簡單的set命令將Key為testkey,Value為edisonchou的索引值對資料存入Master中;
(2)在Slave中讀取剛剛在Master寫入的Key的Value資料
①首先,新開一個cmd,並在cmd中跳轉到Slave檔案夾下,使用redis-cli.exe -p 6380命令進入SlaveRedis用戶端命令模式
②其次,通過一個簡單的get命令查詢是否存在Key為testkey的資料
從可以看到,我們的Slave成功擷取了從Master發來的資料更改,並儲存到了自己的資料檔案中,使得我們通過訪問Slave也能夠得到在Master上寫入的資料。
(3)再來看看Master和Redis的服務的cmd視窗,是不是又多了一些日誌資訊:Master將1個changes寫入資料快照檔案,發送給了Slave。而Slave也將這個change存入自己的資料檔案並儲存,也就使得{testkey,edisonchou}的這個資料在兩個Redis服務中都有了,或者說在Slave中成功複製了Master中的這個key/value對。
2.5 在程式中進行簡單資料讀寫測試
(1)建立一個C#的控制台項目,並在專案檔夾中建立一個Lib檔案夾用以存放Redis的.Net驅動(ServiceStack.Redis的dll);
(2)寫入以下的代碼:
1 public static IRedisClientsManager redisClientManager = new PooledRedisClientManager(new string[] 2 { 3 "127.0.0.1:6379","127.0.0.1:6380" 4 }); 5 6 public static IRedisClient redisClient = redisClientManager.GetClient(); 7 8 static void Main(string[] args) 9 {10 if(redisClient != null)11 {12 redisClient.Set<string>("UserName","EdisonChou");13 string userName = redisClient.Get<string>("UserName");14 15 Console.WriteLine("Hello,My name is {0}.Nice to meet you!", userName);16 }17 Console.ReadKey();18 }
通過調試運行,可以得到以下的結果:
(3)再通過Master和Slave的命令列用戶端查看儲存情況:
①Master中:get UserName
②Slave中:get UserName
三、回頭再看Redis主從複製模型3.1 Redis的兩種儲存方式
Redis是一個支援持久化的記憶體資料庫,如何?持久化呢?答案是Redis需要經常將記憶體中的資料同步到硬盤中來保證持久化。那麼,Redis通過什麼方式來儲存資料呢?
Redis支援兩種持久化方式:
(1)SnapsHotting:資料快照,這也是預設的方式。此方式是把資料做一個備份,將資料存放區到二進位檔案中去(這裡可以對比本文最開始介紹的MySQL的複製過程)。這個二進位的檔案預設的檔案名稱為dump.rdb。我們還可以通過配置設定自動做快照持久化的方式,比如:我們可以配置Redis在n秒內如果超過m個key鍵修改就自動做快照。
但是,快照方式雖然比較完美,但扔存在一定缺陷:由於快照方式是在一定間隔時間做一次的,所以如果Redis意外宕掉的話,就會丟失最後一次快照後的所有修改。因此在完美主義者的推動下,作者增加了aof方式,也就是下面我們所要介紹的方式。
(2)Append-Only File:縮寫為aof,意為只增檔案。此方式在寫入記憶體資料的同時將操作命令儲存到記錄檔(預設命名為appendonly.aof),在Redis遇到意外情況後重啟時可以通過記錄檔恢複資料庫狀態。但是,正因為如此,在一個並發更改上萬的系統中,命令日誌是一個非常龐大的資料(記錄檔會越來越大),管理維護成本非常高,恢複重建時間也會非常長,這樣會導致失去aof高可用性本意(aof的本意其實是資料可靠性及高可用性)。另外更重要的是Redis是一個記憶體資料結構模型,所有的優勢都是建立在對記憶體複雜資料結構高效的原子操作上,這樣就看出aof是一個非常不協調的部分。
剛剛我們說到,Redis預設的儲存方式快照方式,那麼如果我們要開啟aof模式呢?只需要在redis.conf設定檔中將aof模式從no改為yes即可。
3.2 Redis的資料同步流程
首先,Redis的複製功能是完全建立在之前瞭解的基於記憶體快照的持久化策略基礎上的,也就是說無論你的持久化策略選擇的是什麼,只要用到了Redis的複製功能,就一定會有記憶體快照發生。
(1)Slave端在設定檔中添加了slave of <masterip> <masterport>的指令,於是Slave啟動時讀取設定檔並向Master發送SYNC的命令,然後等待Master發送回其記憶體快照檔案;
(2)首先,先說明一下:無論是第一次串連還是重新串連,Master都會啟動一個後台進程,將資料快照儲存到資料檔案(例如:dump.rdb)中,同時Master會記錄所有修改資料的命令並緩衝在資料檔案中。這裡緊接第一步,Master接收到Slave發來的SYNC命令後,會首先向Slave發送一個PING命令來檢測Slave的存活狀態(主要看Slave是否失效,沒有失效則繼續後續操作,失效了則不繼續了)。然後,(當後台進程完成資料快取作業後)Master就發送資料檔案依次地即時發送給Slave。
PS:不管什麼原因導致Slave和Master斷開重連都會重複以上過程。
(3)Slave接收到Master發來的資料檔案之後,會儲存到本地,待接收完成後,載入到記憶體中,這就完成了一次資料複製。之後,Master只要一有資料更新,便會寫入資料檔案並發送給各個Slave,而Slave也會一直監聽Master發來的更新,並重新載入,形成一個資料同步的迴圈。
(4)若Slave出現故障導致宕機,恢複正常後會自動重新串連,Master收到Slave的串連後,將其完整的資料檔案發送給Slave,如果Mater同時收到多個Slave發來的同步請求,Master只會在後台啟動一個進程儲存資料檔案,然後將其發送給所有的Slave,確保Slave正常。但是,在大資料量下,重新擷取整個完整的Master快照,一方面Slave恢複的時間會非常慢,另一方面也會給主庫帶來壓力。
四、學習小結
本篇我首先簡單介紹了一下主從複製架構的基本概念,然後在Windows上單機類比實現Redis的主從複製架構並通過資料讀寫命令進行簡單測試並驗證資料是否複製成功,最後通過瞭解Redis的主從複製模型知道了Redis是如何進行資料複製的,從基礎理論到基礎實踐再到基礎理論,對於主從複製也不算陌生了(至少混了個臉熟)。不知不覺又2:30了,洗洗睡吧,今天(8月2日)還是七夕情人節,誰有多餘的情人借用一下,我保證8月3日奉還(為毛我還木有女盆友,天理不容啊!)。
最後,如果各位園友覺得我的文章不錯或者對你有用,麻煩點一下“推薦”,讓我更有動力寫下去,謝謝!
參考文獻
(1)Linux中國,《最大的Redis叢集:新浪Redis叢集揭秘》,http://www.linuxeden.com/html/itnews/20131010/144377.html
(2)田琪,《Redis複製於可擴充叢集搭建》,http://www.infoq.com/cn/articles/tq-redis-copy-build-scalable-cluster
(3)Stephen Liu,《Redis學習手冊(Key操作手冊)》,http://www.cnblogs.com/stephen-liu74/archive/2012/03/26/2356951.html
(4)zfl092005,《Redis主從配置及主從切換》,http://blog.csdn.net/zfl092005/article/details/17523945
(5)晨風微涼,《構建高效能資料庫緩衝之Redis主從複製》,http://database.51cto.com/art/201407/444555.htm
(6)傳智播客王承偉,《資料最佳化之Redis公開課》,http://bbs.itcast.cn/thread-26525-1-1.html
(7)美舞映璜,《Redis設定檔redis.conf說明》,http://blog.sina.com.cn/s/blog_636415010101970j.html
附件下載
(1)RedisDemo.MasterAndSlave:http://pan.baidu.com/s/1dD1nIgT
轉自:http://www.cnblogs.com/edisonchou/p/3883763.html
【轉】 NoSQL初探之人人都愛Redis:(4)Redis主從複製架構初步探索