少點程式碼,多點頭髮
本文已經被GitHub收錄,歡迎大家踴躍star 和 issues。
https://github.com/midou-tech/articles
入職第一週,我被坑了
最近剛入職新公司,本來想著這剛來新公司,一般都是熟悉熟悉公司同事,看看組內工程文件,找幾個demo自己練練手。
咳咳咳,萬萬沒想到啊,一切都是我以為的,我還是太嫩了。
入職那天下午,組長給我丟了幾個文件,讓我看下這個這些工程的快取系統問題,讓我把redis升級為哨兵模式。
接到任務的我,內心是懵逼的。
第一、不知道都是些什麼型別的服務在用redis。
第二、不知道以什麼姿勢在用redis。
第三、如果redis掛了會不會影響使用者。
第四、我完全沒用過redis。
雖說沒幹過,但咋也不慫。畢竟要是天天干的都是幹過的工作,那就是有問題了,很快就被優化掉了。
看來社招入職和校招還是不一樣的,校招進來都會有些入職培訓或者新人班課程。
通過這些形式的教育,第一、瞭解公司的文化、價值觀,第二、學習工作流程、感受公司技術氛圍。
任務
把我們部門所有使用redis服務升級到哨兵模式。
redis的多種模式
都說了升級到哨兵模式,那之前用的不是哨兵模式,肯定還有其他模式。
單機模式、主從模式、哨兵模式、叢集模式
單機模式
這個最簡單,一看就懂。
就是安裝一個redis,啟動起來,業務呼叫即可。具體安裝步驟和啟動步驟就不贅述了,網上隨便搜一下就有了。
單機在很多場景也是有使用的,例如在一個並非必須保證高可用的情況下。
咳咳咳,其實我們的服務使用的就是redis單機模式,所以來了就讓我改為哨兵模式。
說說單機的優缺點吧。
優點:
部署簡單,0成本。 成本低,沒有備用節點,不需要其他的開支。 高效能,單機不需要同步資料,資料天然一致性。
缺點:
可靠性保證不是很好,單節點有當機的風險。 單機高效能受限於CPU的處理能力,redis是單執行緒的。
單機模式選擇需要根據自己的業務場景去選擇,如果需要很高的效能、可靠性,單機就不太合適了。
主從複製
主從複製,是指將一臺Redis伺服器的資料,複製到其他的Redis伺服器。
前者稱為主節點(master),後者稱為從節點(slave);資料的複製是單向的,只能由主節點到從節點。
主從模式配置很簡單,只需要在從節點配置主節點的ip和埠號即可。
slaveof <masterip> <masterport>
# 例如
# slaveof 192.168.1.214 6379
啟動主從節點的所有服務,檢視日誌即可以看到主從節點之間的服務連線。
從上面很容易就想到一個問題,既然主從複製,意味著master和slave的資料都是一樣的,有資料冗餘問題。
在程式設計上,為了高可用性和高效能,是允許有冗餘存在的。這點希望大家在設計系統的時候要考慮進去,不用為公司節省這一點資源。
對於追求極致使用者體驗的產品,是絕對不允許有當機存在的。
主從模式在很多系統設計時都會考慮,一個master掛在多個slave節點,當master服務當機,會選舉產生一個新的master節點,從而保證服務的高可用性。
主從模式的優點:
一旦 主節點當機,從節點 作為 主節點 的 備份 可以隨時頂上來。
擴充套件 主節點 的 讀能力,分擔主節點讀壓力。
高可用基石:除了上述作用以外,主從複製還是哨兵模式和叢集模式能夠實施的基礎,因此說主從複製是Redis高可用的基石。
也有相應的缺點,比如我剛提到的資料冗餘問題:
一旦 主節點當機,從節點 晉升成 主節點,同時需要修改 應用方 的 主節點地址,還需要命令所有 從節點 去 複製 新的主節點,整個過程需要 人工干預。 主節點 的 寫能力 受到 單機的限制。 主節點 的 儲存能力 受到 單機的限制。
哨兵模式
剛剛提到了,主從模式,當主節點當機之後,從節點是可以作為主節點頂上來,繼續提供服務的。
但是有一個問題,主節點的IP已經變動了,此時應用服務還是拿著原主節點的地址去訪問,這...
於是,在Redis 2.8版本開始引入,就有了哨兵這個概念。
在複製的基礎上,哨兵實現了自動化的故障恢復。
如圖,哨兵節點由兩部分組成,哨兵節點和資料節點:
哨兵節點:哨兵系統由一個或多個哨兵節點組成,哨兵節點是特殊的redis節點,不儲存資料。 資料節點:主節點和從節點都是資料節點。
訪問redis叢集的資料都是通過哨兵叢集的,哨兵監控整個redis叢集。
一旦發現redis叢集出現了問題,比如剛剛說的主節點掛了,從節點會頂上來。但是主節點地址變了,這時候應用服務無感知,也不用更改訪問地址,因為哨兵才是和應用服務做互動的。
Sentinel 很好的解決了故障轉移,在高可用方面又上升了一個臺階,當然Sentinel還有其他功能。
比如 主節點存活檢測、主從執行情況檢測、主從切換。
Redis的Sentinel最小配置是 一主一從。
說下哨兵模式監控的原理
每個Sentinel以 每秒鐘 一次的頻率,向它所有的 主伺服器、從伺服器 以及其他Sentinel例項 傳送一個PING 命令。
如果一個 例項(instance)距離最後一次有效回覆 PING命令的時間超過 down-after-milliseconds 所指定的值,那麼這個例項會被 Sentinel標記為 主觀下線。
如果一個 主伺服器 被標記為 主觀下線,那麼正在 監視 這個 主伺服器 的所有 Sentinel 節點,要以 每秒一次 的頻率確認 該主伺服器是否的確進入了 主觀下線 狀態。
如果一個 主伺服器 被標記為 主觀下線,並且有 足夠數量 的 Sentinel(至少要達到配置檔案指定的數量)在指定的 時間範圍 內同意這一判斷,那麼這個該主伺服器被標記為 客觀下線。
在一般情況下, 每個 Sentinel 會以每 10秒一次的頻率,向它已知的所有 主伺服器 和 從伺服器 傳送 INFO 命令。
當一個 主伺服器 被 Sentinel標記為 客觀下線 時,Sentinel 向 下線主伺服器 的所有 從伺服器 傳送 INFO 命令的頻率,會從10秒一次改為 每秒一次。
Sentinel和其他 Sentinel 協商 主節點 的狀態,如果 主節點處於 SDOWN`狀態,則投票自動選出新的主節點。將剩餘的 從節點 指向 新的主節點 進行 資料複製。
當沒有足夠數量的 Sentinel 同意 主伺服器 下線時, 主伺服器 的 客觀下線狀態 就會被移除。當 主伺服器 重新向 Sentinel的PING命令返回 有效回覆 時,主伺服器 的 主觀下線狀態 就會被移除。
哨兵模式的優缺點
優點:
哨兵模式是基於主從模式的,所有主從的優點,哨兵模式都具有。 主從可以自動切換,系統更健壯,可用性更高。 Sentinel 會不斷的檢查 主伺服器 和 從伺服器 是否正常執行。當被監控的某個 Redis 伺服器出現問題,Sentinel 通過API指令碼向管理員或者其他的應用程式傳送通知。
缺點:
Redis較難支援線上擴容,對於叢集,容量達到上限時線上擴容會變得很複雜。
我的任務
我部署的redis服務就如上圖所示,三個哨兵節點,三個主從複製節點。
使用java的jedis去訪問我的redis服務,下面來一段簡單的演示程式碼(並非工程裡面的程式碼):
public static void testSentinel() throws Exception {
//mastername從配置中獲取或者環境變數,這裡為了演示
String masterName = "master";
Set<String> sentinels = new HashSet<>();
// sentinel的IP一般會從配置檔案獲取或者環境變數,這裡為了演示
sentinels.add("192.168.200,213:26379");
sentinels.add("192.168.200.214:26380");
sentinels.add("192.168.200.215:26381");
//初始化過程做了很多工作
JedisSentinelPool pool = new JedisSentinelPool(masterName, sentinels);
//獲取到redis的client
Jedis jedis = pool.getResource();
//寫值到redis
jedis.set("key1", "value1");
//讀取資料
jedis.get("key1");
}
具體部署的配置檔案這裡太長了,需要的朋友可以公眾號後臺回覆【redis配置】獲取。
聽起來是入職第二天就部署了任務感覺很難的樣子。
其實現在看來是個so easy的任務,申請一個redis叢集,自己配置下。在把工程裡面使用到redis的地方改一下,之前使用的是一個兩個單機節點。
幹完,收工。
雖然領導的任務完成了,但並不意味著學習redis的路結束了。愛學習的龍叔,繼續研究了下redis的叢集模式。
叢集模式
主從不能解決故障自動恢復問題,哨兵已經可以解決故障自動恢復了,那到底為啥還要叢集模式呢?
主從和哨兵都還有另外一些問題沒有解決,單個節點的儲存能力是有上限,訪問能力是有上限的。
Redis Cluster 叢集模式具有 高可用、可擴充套件性、分散式、容錯 等特性。
Cluster 叢集模式的原理
通過資料分片的方式來進行資料共享問題,同時提供資料複製和故障轉移功能。
之前的兩種模式資料都是在一個節點上的,單個節點儲存是存在上限的。叢集模式就是把資料進行分片儲存,當一個分片資料達到上限的時候,就分成多個分片。
資料分片怎麼分?
叢集的鍵空間被分割為16384個slots(即hash槽),通過hash的方式將資料分到不同的分片上的。
HASH_SLOT = CRC16(key) & 16384
CRC16是一種迴圈校驗演算法,這裡不是我們研究的重點,有興趣可以看看。
這裡用了位運算得到取模結果,位運算的速度高於取模運算。
有一個很重要的問題,為什麼是分割為16384個槽?這個問題可能會被面試官隨口一問
資料分片之後怎麼查,怎麼寫?
讀請求分配給slave節點,寫請求分配給master,資料同步從master到slave節點。
讀寫分離提高併發能力,增加高效能。
如何做到水平擴充套件?
master節點可以做擴充,資料遷移redis內部自動完成。
當你新增一個master節點,需要做資料遷移,redis服務不需要下線。
舉個例子:上面的有三個master節點,意味著redis的槽被分為三個段,假設三段分別是0~7000,7001~12000、12001~16383。
現在因為業務需要新增了一個master節點,四個節點共同佔有16384個槽。
槽需要重新分配,資料也需要重新遷移,但是服務不需要下線。
redis叢集的重新分片由redis內部的管理軟體redis-trib負責執行。redis提供了進行重新分片的所有命令,redis-trib通過向節點傳送命令來進行重新分片。
如何做故障轉移?
假如途中紅色的節點故障了,此時master3下面的從節點會通過 選舉 產生一個主節點。替換原來的故障節點。
此過程和哨兵模式的故障轉移是一樣的。
總結
每種模式都有各自的優缺點,在實際使用場景中要根據業務特點去選擇合適的模式。
redis是一個非常常用的中介軟體,作為一個使用者來說,學習成本一點不高。
如果作為一個很好的中介軟體去研究的話,還是有很多值得學習和借鑑的地方。比如redis的各種資料結構(動態字串、跳躍表、集合、字典等)、高效的記憶體分配(jemalloc)、高效的IO模型等等。
每個點都可以深入研究,在後期設計高併發、高可用系統的時候融入進去。
我是龍叔,一個分享網際網路技術和成長心路歷程的star。