慕課網2021-01-29 Redis6直播筆記 - 下(多線程/哈希slot/集群)

io多線程

以前的redis是單線程模型,其實就是多路復用機制,知道多路復用的來一波6,我們在架構師課程中講過,那么netty也有,看過老師相關課程的也應該知道。這里不多說了。

Redis6開始有了IO讀寫多線程,只不過執行用戶的命令和早期版本也是一樣的,都是單線程執行,所以線程安全。

我們先來看一下老版本的單線程:


-w1750

首先讀取客戶端的命令,讀取后執行命令,然后回寫給客戶端,這個就是一組命令的執行,由于單線程安全,他們會一組一組的去進行執行。

他們的讀寫命令以及執行命令都是在一個線程中執行的,這個線程在redis6中稱之為主線程。

在這里我們可以回顧一下netty的reactor的線程模型,也就是多路復用。


-w1505

你可以把這個單線程理解為是一個人,他是酒吧會所的接待員,在門口接待了以后,然后還要領到里面去一個一個的招待他們。

我們可以打開redis.conf配置文件,看一下:


-w1216

默認情況下,是按照老版本的樣子,如果要使用多線程,那么開啟即可,這里的io-threads就是設置多線程的數量,開啟多線程后,整體的性能要比單線程要更高。

io-threads設置的數量最大不建議超過8,提升的空間不大。另外線程數和服務器的硬件配置也是有關系的。比如4核8g,那么建議設置2或者3,如果8核建議設置6或者7,因為一定要預留,萬事兜底萬事保底,這一點和nginx的配置也是同樣的道理。

假設現在io-threads設置為4,那么他的模型就如下:

-w1339

那么在這里,讀用戶的命令以及執行命令都是在一個線程中執行的,然后寫操作是多線程執行。在這里如果比作是一個銀行的話,那么讀操作相當于是門口的保安,給你測個體溫,執行就是店里的大堂經理,會帶你去取號,多個寫操作就是窗口的辦事人員。

可以參考一下圖:


-w1704

剛剛我們舉例是只有一個人在接待和招待,這個時候接待只有一個人,他只在門口把客人帶進里面,里面會有專門的多個招待員來處理這些客人的請求。

讀操作能不能設置多線程呢?

-w1072

如果想要讀操作也變成多線程讀的話,那么io-threads-do-reads可以開啟,設置為yes就行。這個時候他的模型就是下面這張圖:

-w1327

只不過官方說明,這個多線程讀開啟的意義不大,不會幫助很多的。


-w1672

這個時候,還是引入剛剛的例子,客人多了,生意好了,門口的接待員增加了,那么兩邊接待和招待都是多個員工在處理客人了。

那么在這里說明一點,多線程只針對數據的讀寫以及協議的解析。真正用戶端的命令執行還是單線程,所以是線程安全的。

Redis集群原理

-w1327

其實只要涉及到中間件,那么必定會有集群的概念,一方面為了高可用,一方面為了達到水平擴容,那么這次來講一下redis的集群。

其實在很早以前,早期的redis版本是沒有集群這個概念的,你需要實現集群得依靠一些中間件,比如codis,還有twemproxy。redis集群概念是在3.0開始引入的,它是自帶的分布式存儲方案,是一個去中心化的集群,叫做Redis Cluster,是由多個主從節點共同存在的一個模式,一般以3主3從為比較經典的模式,當然多主多從也可以。其中master負責讀寫請求以及整個集群信息的維護,slave只做他所對應的主節點數據和狀態信息的復制。

Redis Cluster

關于集群,有兩點需要注意

  • 之前我們聊過主從模式,可以做讀寫分離,redis集群雖然是3主3從,也有主從的概念,但是我們并不會做讀寫分離,讀寫都是交給master去處理,數據會同步給slave,如果集群做讀寫分離一方面沒有意義一方面slave就沒有容錯機制了,這一點是需要注意。
  • 此外還有一點,單節點的redis默認有16個db,但是在集群模式下,這些db都融合了,沒有db庫的概念,他是一片汪洋大海。

按照單節點,主從,哨兵來說的話,redis始終都可以說是單庫,數據存儲量是有上限的,你的服務器節點內存有多大,那么這個就是存儲上限。一旦到達存儲上限,redis就會進行緩存key的自我淘汰機制。很明顯,這種方式面對海量數據的時候并不太好,哪怕你買1個t內存的,也總有一天會到達存儲瓶頸,所以任何分布式系統我們都要考慮一個水平擴容機制,這個redis集群就可以做到。擴容的同時也滿足高可用機制。

同時,使用集群之后,數據其實進行了分片,多個master節點都能夠提供讀寫服務,這樣整體集群的響應能力就要比原先單節點來的更好。并且,在集群模式下,任意節點發生故障時,集群還是可以繼續對外提供服務,主節點有故障轉移的功能。

傳統的水平擴容

傳統早期的redis擴容方案其實是基于業務層的,在業務層進行redis的分割,可以擴容,可以集群隔離。
我們之前講過redis主從,以這個為例,當然用哨兵也可以,我們可以手動來實現redis的擴容,先看下圖:


-w1712

上圖中的3主3從其實是由3個主從redis構成的,用戶在進行set或者get的時候,首先需要對key做哈希,哈希后的值對節點數求模,取模的值就是數據路由到某個主從庫里,就針對這個主從庫去做set和get的操作,這個哈希原理其實和nginx的ip_hash道理是一樣的,數據庫分庫也是這個道理。目的就是根據key哈希后的結果去尋址找到最終他的存儲位置去存值或取值。這個其實就是哈希取模

那么哈希以及尋址的這個過程需要自己在業務層去封裝實現,但是本質目的就已經達到了橫向擴容,假設每個主從容量是8g,那么目前總容量就是24g,想怎么擴容直接加機器就行。


-w569

這這樣的情況之下,那么擴容會帶來一個弊端,這個弊端也就是哈希取模帶來的,一旦有節點宕機或者增加節點,那么就需要重新哈希和求模運算,這么一來會影響所有的緩存數據,這個時候所有數據會重新路由,影響會很大,怎么解決呢?可以使用一致性哈希算法,就是小部分數據受影響,這個我們在架構班里有提到過的吧。

一致性哈希

-w1620

首先他會有一個域,這個域很長,有2的32次方減1,哈希不僅針對key,也會針對服務器節點,我們這里是redis,如果是服務器集群那么道理也是一樣。


-w1736

Redis集群 - hash slot 算法

上面的傳統方式是很古老的,我曾經也見識過一次,我們了解一下就行了。現在都使用redis cluster,這種模式下,redis可以有多個master節點,每個master節點下又可以對應1~n個slave節點,這樣會形成一個多主多從的集群模式,這種模式下不再需要向上面講過的那樣需要在業務層去處理,redis自身幫我們做好了存取的過程,我們可以不用過多關注業務過程就可以直接使用和應用了。他自身是基于hash slot的算法來存值的,不論是哪種算法,其實本質為了解決的就是數據分布的問題。

redis集群其實是把數據分片了,由于他有多個master共同構成,他會分數據,他有一個hash slot算法,也可以稱之為哈希槽,每個槽上存放了一些數據,slot的范圍是0~16384,redis對key的存取會有一個CRC16的算法,得到的值,在對16384做取模,最終判斷該key應該在哪個slot中進行存取,本質和服務器節點數取模是一樣的,只不過redis cluster的slot算法更精妙,成本更低廉。

以三主三從為例,這個時候的slot總數會平均等份,使得每個master節點中都會有slot,目前3個的話那就是{0..5461}{5462..10922}{10923..16383},這個取值區間在搭建好集群之后是可以看到的。

如果這個時候,集群環境增加或者減少節點,slot可以重新遷移或者合并,那么slot中的緩存key其實還是存在的,如此一來,節點宕機或者新增就不會造成緩存丟失了。這就相當于家里房子拆遷,我到新家里以后,會把我的私人物品家具啊電器啊一起攜帶過去,東西跟著人走,東西就是緩存key,人就slot,服務器節點是redis實例,比較靈活。并不是說你房子沒了,家里的東西就沒了。


-w1671

提問:搬家的過程,新家能住人嗎?
當然,你搬家并不是馬上就能完成的,搬家需要耗時,需要等待,所以等待期間你的家具啊電器啊不可用。redis slot也是這樣,宕機的時候,那部分需要遷移的slot是不可用,會有一個短暫的遷移的過程,等待遷移完成之后,才能為用戶提供讀寫服務。

hash slot 圖示:


image

如下圖:中間的master宕機了,slot會遷移到slave


-w1054

如果,新增master節點,重新分配,那么會把一些slot遷移過去,緩存數據不變,跟著slot走,雖然slot會變動到其他的master節點,但是數據key哈希的時候,還是會到跟著固定的slot。

格子鋪

如果還不能理解,那就再舉個栗子,格子鋪,每個格子歸屬一個賣家,這個賣家把各自托管給某些商鋪,由他們代運營,如果商鋪倒閉,格子還是存在的,他可以把各自帶著走,各自里的商品就是redis集群中的數據呀,它是不會發生更改的,只是外殼換了一家商鋪而已。

Redis集群搭建

時間關系,我們在這里就只講原理了,操作部分,我們提供了慕課網的手記文檔,很簡單,大家可以去看一下就行。
鏈接地址:https://www.imooc.com/article/313301

只不過在集群中有一些注意點,我們大致總結了如下:

  1. 讀寫都是在master,slave加入集群,會進行數據同步,連接集群中的任意主或從節點去讀寫數據,都會根據key哈希取模后路由到某個master節點去處理。slave不提供讀寫服務,只會同步數據。

  2. 關閉任意一主,會導致部分寫操作失敗,是由于從節點不能執行寫操作,在Slave升級為Master期間可能會有少量的失敗。

  3. 關閉從節點對于整個集群沒有影響

  4. 某個主節點和他麾下的所有從節點全部掛掉,我們集群就進入faill狀態,不可用。因為slot不完整。

  5. 如果集群超過半數以上master掛掉,無論他們是否有對應slave,集群進入fail狀態,因為無法選舉。

  6. 如果集群中的任意master宕機,且此master沒有slave。集群不可用。(同3)

  7. 投票選舉過程是集群中所有master參與,如果半數以上master節點與master節點通信超時(cluster-node-timeout),認為當前master節點掛掉。

  8. 選舉只會針對某個master下的所有slave選舉,而不是對所有全量的slave選舉。

  9. 原先的master重新恢復連接后,他會成為新master的從服務器。由于主從同步,客戶端的寫入命令,有可能會丟失。redis并非強一致性,由于主從特性,所以最后一部分數據會丟失。這也符合CAP理論。

  10. 集群只實現了主節點的故障轉移;從節點故障時只會被下線,不會進行故障轉移。因此,使用集群時,一般不會使用讀寫分離技術,因為從節點故障會導致讀服務不可用,可用性變差了。所以不要在集群里做讀寫分離。

需要注意,為了保證集群的完整性,只有當16384個槽slot完全的全部分配完畢,集群才可以上線。而且,如果主節點發生故障轉移,并且處于故障轉移過程中還未完成時,原主節點的槽slot不會在任何節點中,集群會處于下線狀態,客戶端無法調用。CAP原理。

Redis集群數據遷移

已有數據的單節點的redis如何擴展為集群

搭建集群的時候,包清除節點中現有的aof以及rdb文件,那么如果現在本來就是單節點,那么如何擴展為集群呢?并且要保證數據不丟失。其實原理就是把rdb或者aof文件保存以后,再導入,然后通過slot來分配。具體步驟如下:


-w1098

如上圖,其原理就是slot遷移。整個過程阻塞,網站服務不對外提供服務。如果使用自建集群,那么運維成本很高,一般采用云redis的話會更好。哪怕阿里云,也會在配置變更的時候出現網絡中斷的情況。


-w732
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 文章大綱 一、基礎知識學習二、Redis常見的幾種架構及優缺點總結三、Redis之Redis Sentinel(哨...
    故事愛人c閱讀 1,136評論 3 12
  • redis redis是單線程的,但是一般的作為緩存使用的話,redis足夠了,因為它的讀寫速度太快了。官方的一個...
    普度眾生的面癱青年閱讀 5,150評論 0 4
  • 久違的晴天,家長會。 家長大會開好到教室時,離放學已經沒多少時間了。班主任說已經安排了三個家長分享經驗。 放學鈴聲...
    飄雪兒5閱讀 7,573評論 16 22
  • 今天感恩節哎,感謝一直在我身邊的親朋好友。感恩相遇!感恩不離不棄。 中午開了第一次的黨會,身份的轉變要...
    迷月閃星情閱讀 10,613評論 0 11
  • 可愛進取,孤獨成精。努力飛翔,天堂翱翔。戰爭美好,孤獨進取。膽大飛翔,成就輝煌。努力進取,遙望,和諧家園。可愛游走...
    趙原野閱讀 2,778評論 1 1