日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

Redis?分片集群的實現_Redis

作者:左右盲 ? 更新時間: 2023-03-26 編程語言

1 搭建分片集群

主從和哨兵可以解決高可用、高并發讀的問題。但是依然有兩個問題沒有解決:

  • 海量數據存儲問題,單個Redis節點對于數據的存儲量是有上限的
  • 高并發寫的問題,高并發讀的問題我們可以用主從集群來解決,那高并發寫的問題又該怎樣解決呢

針對上述問題,我們可以搭建Redis的分片集群,如圖所示:

Redis的分片集群具有以下特征:

  • 集群中有多個master,每個master保存不同數據
  • 每個master都可以有多個slave節點
  • master之間通過ping監測彼此健康狀態(可以取代哨兵機制)
  • 客戶端請求可以訪問集群任意節點,最終都會被轉發到正確節點

接下來我們可以動手來搭建一個Redis分片集群

1.1 集群結構

分片集群需要的節點數量較多,這里我們搭建一個最小的分片集群,包含3個master節點,每個master包含一個slave節點,結構如下:

這里我們會在同一臺虛擬機中開啟6個redis實例,模擬分片集群,信息如下:

IP PORT 角色
192.168.211.100 7001 master
192.168.211.100 7002 master
192.168.211.100 7003 master
192.168.211.100 8001 slave
192.168.211.100 8002 slave
192.168.211.100 8003 slave

1.2 準備實例和配置

這里我的redis安裝目錄為/usr/local/redis-6.2.6,以下操作將以此目錄進行參考,額外需要注意的是,以下操作都是在redis沒有設置密碼的情況下進行的,如果你的redis設置了密碼,那么按照以下步驟進行就會出問題。

1)創建出7001、7002、7003、8001、8002、8003目錄

# 進入/local目錄
cd /usr/local
# 創建目錄
mkdir 7001 7002 7003 8001 8002 8003

2)在/usr/local下準備一個新的redis.conf文件,內容如下:

port 6379
# 開啟集群功能
cluster-enabled yes
# 集群的配置文件名稱,不需要我們創建,由redis自己維護
cluster-config-file /usr/local/6379/nodes.conf
# 節點心跳失敗的超時時間
cluster-node-timeout 5000
# 持久化文件存放目錄
dir /usr/local/6379
# 綁定地址
bind 0.0.0.0
# 讓redis后臺運行
daemonize yes
# 注冊的實例ip
replica-announce-ip 192.168.211.100
# 保護模式
protected-mode no
# 數據庫數量
databases 1
# 日志
logfile /usr/local/6379/run.log

3)將這個文件拷貝到每個目錄下:

# 進入/local目錄
cd /usr/local
# 執行拷貝
echo 7001 7002 7003 8001 8002 8003 | xargs -t -n 1 cp redis.conf

4)修改每個目錄下的redis.conf,將其中的6379修改為與所在目錄一致:

# 進入/local目錄
cd /usr/local
# 修改配置文件
printf '%s\n' 7001 7002 7003 8001 8002 8003 | xargs -I{} -t sed -i 's/6379/{}/g' {}/redis.conf

1.3 啟動

因為已經配置了后臺啟動模式,所以可以直接啟動服務:

# 進入/usr/local目錄
cd /usr/local
# 一鍵啟動所有服務
printf '%s\n' 7001 7002 7003 8001 8002 8003 | xargs -I{} -t redis-server {}/redis.conf

通過ps查看狀態:

ps -ef | grep redis

發現服務都已經正常啟動:

如果要關閉所有進程,可以執行命令:

ps -ef | grep redis | awk '{print $2}' | xargs kill

或者(推薦這種方式):

printf '%s\n' 7001 7002 7003 8001 8002 8003 | xargs -I{<!--{cke_protected}{C}%3C!%2D%2D%20%2D%2D%3E-->} -t redis-cli -p {<!--{cke_protected}{C}%3C!%2D%2D%20%2D%2D%3E-->} shutdown

1.4 創建集群

雖然服務啟動了,但是目前每個服務之間都是獨立的,沒有任何關聯。我們需要執行以下命令來創建集群,注意,以下命令需要你的redis版本在5.0之后:

redis-cli --cluster create --cluster-replicas 1 192.168.211.100:7001 192.168.211.100:7002 192.168.211.100:7003 192.168.211.100:8001 192.168.211.100:8002 192.168.211.100:8003

命令說明:

  • redis-cli --cluster:代表集群操作命令
  • create:代表創建集群
  • --cluster-replicas 1 :指定集群中每個master的副本個數為1,也就是說一個master只有一個slave,此時節點總數 ÷ (replicas + 1) 得到的就是master的數量。因此節點列表中的前n個就是master,其它節點都是slave節點,在創建集群時這些slave會被隨機分配給不同master

執行上述命令之后,控制臺會列出當前主從節點分配的結果,即將那些slave分別分配給哪些master,并詢問你是否同意,這里我們輸入'yes'即可

確定之后,集群開始創建

通過命令可以查看集群狀態:

redis-cli -p 7001 cluster nodes

1.5 測試

集群操作時,需要在redis-cli連接時帶上-c參數才可以

redis-cli -p 7001

通過觀察上述從節點的狀態,我們發現7001的slave是8001,我們可以嘗試在7001里插入一個數字,再從8001里獲取

事實上,我們不僅可以從8001里獲取到num,也可以從其他slave甚至其他master里獲取到num:

而且我們發現,當我們試圖從其他節點獲取num時,最后都會跳轉到7001,為什么會這樣呢?這就涉及到我們即將講解的插槽原理

2 散列插槽

一個Redis分片集群有0~16383共16384個插槽(hash slot),這些插槽會被平均分給每一個master節點,一個master節點映射著一部分插槽,這一點在集群創建時的信息中可以看到

在分片集群中,數據key并不是與某個節點綁定,而是與插槽綁定。數據key與插槽是多對一的關系,redis會根據key的有效部分計算插槽值,然后將key放入對應插槽,key的有效部分分兩種情況:

  • 當key中包含"{}“時,且”{}“中至少包含1個字符,”{}"中的部分是有效部分
  • key中不包含"{}",整個key都是有效部分

舉個例子,假如key是num,那么插槽值就會根據num來計算,如果key是{itheima}num,那么插槽值就會根據itheima來計算。計算方式是利用CRC16算法得到一個hash值,然后對16384取余,得到的結果就是slot值。

如果當前操作的key所在的插槽不屬于本節點,則會發生重定向,重定向的目標是該插槽所屬的節點,這個節點一定是master,如果我們連接的節點為slave,則會直接進行重定向,因為slave是沒有插槽的。

針對上述幾點,演示如下:

如上圖所示,我們連接了7001,并試圖插入數據k1,這時redis需要去判斷k1所屬的插槽位置,由于key中不包含’{}',因此整個key都是有效部分,redis會對k1做hash運算然后對16384取余,得到的結果是12706,這也就是k1所在的插槽的位置,在當前集群中,映射該插槽的節點是7003,此時就會發生重定向,我們也可以觀察到當我們執行完set k1 1命令之后,操作的端口已經變成了7003。此時我們繼續在7003端口中進行操作,比如修改數據num的值,而num所在的插槽是2765,在當前集群中,映射該插槽的節點是7001,因此當我們執行完set num 2命令之后,操作的端口又重新變成了7001

那么接下來讓我們思考兩個問題:

為什么數據key要與插槽綁定,而不是與節點綁定呢?

這是因為Redis的主節點有可能會出現宕機情況,也有可能由于集群伸縮而被刪除,當節點刪除或者發生宕機時,節點上保存的數據也就丟失了,但如果數據綁定的是插槽而不是節點,那么當出現上述情況時,就可以將故障節點的插槽轉移至存活節點上。這樣,數據跟插槽綁定,就永遠都能夠找到數據所在位置。

如何將同一類數據固定的保存在一個插槽中

在業務開發中,同一類型的數據key最好是保存在一個插槽中,因為如果分散保存,在我們調用的時候就很可能出現重定向的情況,重定向是會消耗一部分性能的。如果我們希望將同一類型的數據key最好是保存在一個插槽中,可以為這些key帶上一個用’{}'包裹的固定前綴,比如{user}zs、{user}ls等等,因為我們之前說過,當key中包含"{}“,且”{}“中至少包含1個字符時,”{}"中的部分是有效部分,redis會根據這一部分來計算插槽值,如果我們將同一類型的key都加上這類前綴,就能保證這些key在同一個插槽中了

3 集群伸縮

集群已經創建了,那么如果我們想在集群中添加節點或刪除節點,又應該怎么做呢?

redis-cli --cluster提供了很多操作集群的命令,可以通過下面方式查看:

其中就包括添加節點的命令:

假設現在有以下需求:向集群中添加一個新的master節點7004,并在這個節點中存儲 num = 10,執行步驟如下:

3.1 創建節點并添加到集群

1)在/usr/local目錄下創建一個文件夾:

cd /usr/local
mkdir 7004

2)拷貝配置文件:

cp redis.conf 7004

3)修改配置文件:

printf '%s\n' 7004 | xargs -I{<!--{cke_protected}{C}%3C!%2D%2D%20%2D%2D%3E-->} -t sed -i 's/6379/{}/g' {<!--{cke_protected}{C}%3C!%2D%2D%20%2D%2D%3E-->}/redis.conf

4)啟動

redis-server 7004/redis.conf

接下來就需要將7004添加到集群中了,在執行添加操作之前,我們先來了解以下添加節點的命令

添加節點首先需要以下幾個參數:

  • new_host:new_port :指定新添加的節點的ip地址與端口號,這個沒什么好說的
  • existing_host:existing_port:任意指定一個集群中已經存在的節點的ip地址與端口號。因為集群中加入新節點是需要通知其他舊節點的,新節點只需要將自己的信息提供給集群中任意一個節點,那么整個集群就都能知道關于新節點的信息了
  • cluster-slave:可選項,不指定則表示該節點是master,如果指定了則表示該節點是一個slave
  • cluster-master-id <arg>:如果我們指定了cluster-slave,那么就需要通過該參數指定該節點的master的id

了解了該命令之后,接下來我們來執行添加節點操作:

執行命令:

redis-cli --cluster add-node 192.168.211.100:7004 192.168.211.100:7001

通過命令查看集群狀態:

redis-cli -p 7001 cluster nodes

如圖,7004加入了集群,并且默認是一個master節點:

但是,我們也可以看到7004是沒有插槽的,因為插槽已經被其他master瓜分完畢了,因此沒有任何數據可以存儲到7004上,這時候我們就需要進行插槽的轉移,即將其他matser的插槽分出一部分給7004

3.2 轉移插槽

首先回歸需求本身,我們的需求是將num=10存儲在7004節點上,那么我們的目的就很明顯了,首先需要知道num存儲在哪個插槽上,然后將這個插槽轉移到7004上即可

如上圖所示,num的插槽為2765,該插槽目前是保存在7001上的,因此我們可以將0~3000的插槽從7001轉移到7004,轉移插槽的命令格式如下:

具體步驟如下:

1)輸入轉移插槽命令,這里我們需要轉移的插槽在7001上,因此需要指定7001的地址

redis-cli --cluster reshard 192.168.211.100:7001

2)系統會詢問我們要移動多少個插槽,這里我們輸入3000即可

3)系統接著詢問我們需要讓哪個節點來接收插槽,這里我們需要輸入7004的ID

4)接著系統會詢問我們要從哪些節點中移動這些插槽到7004

這里我們有三個選擇:

  • all:代表全部,也就是三個節點各轉移一部分
  • 具體的id:目標節點的id
  • done:表示結束

這里我們需要從7001中獲取插槽,因此填寫7001的id,然后輸入done表示結束

5)接下來會冒出一大串東西,并詢問我們是否確定要移動這些插槽,這里我們直接輸入yes即可

輸入yes之后,等待控制臺打印結束,插槽也就移動完畢了

6) 輸入以下命令查看插槽是否已經移動到7004

redis-cli -p 7001 cluster nodes

很顯然,我們的目的已經達成了

那么如果我們要刪除7004節點,又應該怎樣做呢?這里筆者只給去具體思路,大家可以自行嘗試一下:

  • 先將 7004 分配的插槽轉移至其他節點
  • 執行刪除節點命令redis-cli --cluster del-node host:port node_id
  • 通過命令查詢結果redis-cli -p 7001 cluster nodes

4 故障轉移

之前我們提到過,redis分片集群可以通過master之間的心跳監測來監測彼此之間的健康狀態,從而取代哨兵。而我們也知道,哨兵的作用就是監測master和slave的狀態,當認為一個master客觀下線后,會從slave中選舉出一個新的master,現在讓我們來驗證一下redis分片集群是否具備這個功能。

首先集群的初始狀態是這樣的,如果狀態為connected則表示節點正常連接

其中7001、7002、7003、7004都是master,我們計劃讓7002宕機。

4.1.自動故障轉移

當集群中有一個master宕機會發生什么呢?我們可以直接停止一個redis實例,例如7002:

redis-cli -p 7002 shutdown

1)首先是該實例與其它實例失去連接

2)然后是疑似宕機,7002的狀態變成了disconnected

3)最后是確定下線,將7002的一個slave提升為新的master,這里由于7002只有一個slave,即8002,因此8002被選為了新的master

4)接下來我們通過以下命令再次啟動7002節點

redis-server /usr/local/7002/redis.conf

當7002再次啟動之后,它就已經變為一個slave節點了

上面這種叫自動故障轉移,但有的時候我們可能需要做手動故障轉移,比如當某臺master機器比較老舊,需要升級時,我們就可以在這個集群中新增一個節點,讓這個節點成為取代原來的master成為新的master,這樣原來的master就會變成新master的一個slave,從而實現機器的無感知升級

4.2 手動故障轉移

我們可以在slave節點中使用cluster failover命令,這個命令會讓當前slave節點的master暫時宕機,宕機期間會將自身的數據轉移給執行命令的slave節點,宕機結束后,之前的master會變成slave,而執行命令的slave會變成新的master。

其詳細流程如下:

當slave執行了cluster failover命令之后,就會向master節點發送節點替換通知,為了避免數據的丟失,master接收到slave節點發送過來的通知后,就會暫時拒絕來自客戶端的任何數據讀寫請求。然后,master會將自己當前的offset返回給slave,slave接收到后會判斷自身數據中的offset與master的offset是否一致,如果不一致,則需要進行數據同步。由于 master 已經拒絕了客戶端的所有請求,那么一旦 slave完成數據同步,也就表示slave與master之間數據是完全一致的。

數據同步結束之后,就會開始進行故障轉移,讓slave與master 進行角色互換,該slave成為新的master,而舊的master則轉變為一個新的slave。轉移結束后,slave便會標記自己為master,并向集群中每一個節點廣播故障轉移的結果。當集群中節點收到廣播后,后續的所有交互便轉移至新的master。

這種failover命令可以指定三種模式:

缺省:默認的流程,如圖1~6歩,一般我們會選擇這個force:省略了對offset的一致性校驗,直接開始故障轉移takeover:直接執行第5歩,忽略數據一致性、忽略master狀態和其它master的意見

接下來我們可以嘗試一下,在7002這個slave節點上執行手動故障轉移,讓它重新奪回master地位,步驟如下:

1)利用redis-cli連接7002,并執行cluster failover命令

2)通過redis-cli -p 7001 cluster nodes命令查看節點狀態

5 RedisTemplate訪問分片集群

我們只需要通過以下簡單的配置,就可以通過Java代碼訪問我們之前部署好的分片集群

1)在boot項目的pom文件中導入依賴

 <dependency>
 	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>

2)在application.yml中指定sentinel相關信息:

spring:
  redis:
    cluster:
      nodes: #指定分片集群中每一個節點信息
        - 192.168.150.101:7001
        - 192.168.150.101:7002
        - 192.168.150.101:7003
        - 192.168.150.101:8001
        - 192.168.150.101:8002
        - 192.168.150.101:8003

3)在項目的啟動類中,添加一個新的bean,這個bean是用來做Redis集群的讀寫分離的

@Bean
public LettuceClientConfigurationBuilderCustomizer clientConfigurationBuilderCustomizer(){
    return clientConfigurationBuilder -> clientConfigurationBuilder.readFrom(ReadFrom.REPLICA_PREFERRED);
}

bean中配置的信息是讀寫策略,包括四種可選項:

  • MASTER:從master讀取
  • MASTER_PREFERRED:優先從master節點讀取,master不可用才讀取slave
  • REPLICA:從slave節點讀取
  • REPLICA _PREFERRED:優先從slave節點讀取,所有的slave都不可用才讀取master

上述配置完畢之后,我們就可以正常使用RedisTemplate來對redis集群進行操作,如果集群中某個的master宕機了,集群就會自動選舉新的master,并將新master的信息發送給該Java程序,這樣Java程序就可以對新master進行寫操作而對其他節點進行讀操作了。而這一過程都是自動完成的,無需我們過多關注

原文鏈接:https://blog.csdn.net/qq_50675319/article/details/127040803

欄目分類
最近更新