[TOC]
## 對redis集群架構的理解
redis cluster 著眼于可擴展。當單個redis不足時,使用cluster進行分片存儲。
>Redis Cluster 使用分片機制,在內部分為 16384 個 slot 插槽,分布在所有 master 節點上,每個 master 節點負責一部分 slot。數據操作時按 key 做 CRC16 來計算在哪個 slot,由哪個 master 進行處理。數據的冗余是通過 slave 節點來保障。
Redis 的高可用。Redis 支持主從同步,提供 Cluster 集群部署模式,通過 Sentine l哨兵來監控 Redis 主服務器的狀態。當主掛掉時,在從節點中根據一定策略選出新主,并調整其他從 slaveof 到新主。 選主的策略簡單來說有三個:
>* slave 的 priority 設置的越低,優先級越高;
>* 同等情況下,slave 復制的數據越多優先級越高;
>* 相同的條件下 runid 越小越容易被選中。 在 Redis 集群中,sentinel 也會進行多實例部署,sentinel 之間通過 Raft 協議來保證自身的高可用。
# 集群方案
https://mp.weixin.qq.com/s/p5WCAA10OLxKAsE5CrD09w
https://mp.weixin.qq.com/s/fBShKZbuR54yaIzzMR3R7g
https://mp.weixin.qq.com/s/_54mD0EiS9R3CvpDXzN0lg
https://mp.weixin.qq.com/s?__biz=MzkzMDI1NjcyOQ==&mid=2247487789&idx=1&sn=7f8245f8b4e4a98aa0a717011f7b7e24&source=41#wechat_redirect cluster
https://blog.51cto.com/lxw1844912514/2943798 三種模式,包括配置
## 主從模式
主從模式中,Redis部署了多臺機器,有主節點,負責讀寫操作,有從節點,只負責讀操作。從節點的數據來自主節點,實現原理就是**主從復制機制**
> 全量復制的時候,數據量很大時,就會對主從節點和網絡造成很大的開銷,也就是常說的`復制風暴`
主從復制包括全量復制,增量復制兩種。一般當slave第一次啟動連接master,或者認為是第一次連接,就采用**全量復制**,全量復制流程如下:

~~~
1.slave發送psync命令到master。
2.master接收到SYNC命令后,返回runid和offect偏移量
3.從節點保存主節點的runid和偏移量
4.執行bgsave命令,生成RDB全量文件。
5.master使用緩沖區,記錄RDB快照生成期間的所有寫命令。
6.master執行完bgsave后,向所有slave發送RDB快照文件。
7.slave收到RDB快照文件后,載入、解析收到的快照。
8.master向slave發送緩沖區中的寫命令;
9.salve接受命令請求,并執行來自master緩沖區的寫命令
~~~
### 主從復制的作用:
* 數據冗余,實現數據的熱備份
* 故障恢復,避免單點故障帶來的服務不可用
* 讀寫分離,負載均衡。主節點負載讀寫,從節點負責讀,提高服務器并發量
* 高可用基礎,是哨兵機制和集群實現的基礎
### 優點
* Master/Slave 角色方便水平擴展,QPS 增加,增加 Slave 即可;
* 降低 Master 讀壓力,轉交給 Slave 節點;
* 主節點宕機,從節點作為主節點的備份可以隨時頂上繼續提供服務;
### 缺點
* 可靠性保證不是很好,主節點故障便無法提供寫入服務;
* 沒有解決主節點寫的壓力;
* 數據冗余(為了高并發、高可用和高性能,一般是允許有冗余存在的);
* **一旦主節點宕機,從節點晉升成主節點,需要修改應用方的主節點地址,還需要命令所有從節點去復制新的主節點,整個過程需要人工干預;**
* 主節點的寫能力受到單機的限制;
* 主節點的存儲能力受到單機的限制。
## 哨兵模式
sentinel模式是建立在主從模式的基礎上,只是增加了哨兵集群

每個sentinel以每秒鐘一次的頻率向它所知的master,slave以及其他sentinel實例發送一個 PING 命令
### 哨兵機制
* 如果一個實例距離最后一次有效回復 PING 命令的時間超過 down-after-milliseconds 選項所指定的值, 則這個實例會被sentinel標記為主觀下線。
* 如果一個master被標記為主觀下線,則正在監視這個master的所有sentinel要以每秒一次的頻率確認master的確進入了主觀下線狀態
* 當有足夠數量的sentinel(大于等于配置文件指定的值)在指定的時間范圍內確認master的確進入了主觀下線狀態, 則master會被標記為客觀下線
* 在一般情況下, 每個sentinel會以每 10 秒一次的頻率向它已知的所有master,slave發送 INFO 命令
* 當master被sentinel標記為客觀下線時,sentinel向下線的master的所有slave發送 INFO 命令的頻率會從 10 秒一次改為 1 秒一次
* 若沒有足夠數量的sentinel同意master已經下線,master的客觀下線狀態就會被移除;
* 若master重新向sentinel的 PING 命令返回有效回復,master的主觀下線狀態就會被移除
### 哨兵的作用或能力
* 集群監控,即時刻監控著redis的master和slave進程是否是在正常工作。
* 消息通知,就是說當它發現有redis實例有故障的話,就會發送消息給管理員
* 故障自動轉移,如果redis master 節點宕機了的話,它就會將請求轉到slave 節點上,slave升為master。
* 充當配置中心,如果發生了故障轉移,它會通知將master的新地址寫在配置中心告訴客戶端。
### 故障轉移流程
* 第一步:在已下線主節點(舊主節點)屬下的所有「從節點」里面,挑選出一個從節點,并將其轉換為主節點。
* 第二步:讓已下線主節點屬下的所有「從節點」修改復制目標,修改為復制「新主節點」;
* 第三步:將新主節點的 IP 地址和信息,通過「發布者/訂閱者機制」通知給客戶端;
* 第四步:繼續監視舊主節點,當這個舊主節點重新上線時,將它設置為新主節點的從節點;
### 故障轉移由哨兵leader來完成,成為leader的條件?
(候選者:最初認為master下線的哨兵)
* 第一,拿到半數以上的贊成票;
* 第二,拿到的票數同時還需要大于等于哨兵配置文件中的 quorum 值。
### 哨兵之間是如何知道彼此?
哨兵與 master 建立通信,利用 master 提供發布/訂閱機制發布自己的信息,比如IP、端口……
master 有一個`__sentinel__:hello`的專用通道,用于哨兵之間發布和訂閱消息。**這就好比是`__sentinel__:hello`微信群,哨兵利用 master 建立的微信群發布自己的消息,同時關注其他哨兵發布的消息**。
### 哨兵之間雖然建立連接了,如何知道 slave 并監控他們的?
關鍵還是利用 master 來實現,哨兵向 master 發送`INFO`命令, master 掌門自然是知道自己門下所有的 salve 小弟的。所以 master 接收到命令后,便將 slave 列表告訴哨兵。
哨兵根據 master 響應的 slave 名單信息與每一個 salve 建立連接,并且根據這個連接持續監控哨兵。
### 定時任務
Sentinel 內部有 3 個定時任務,分別是:
* 每 1 秒每個 Sentinel 對其他 Sentinel 和 Redis 節點執行`PING`操作(監控),這是一個**心跳檢測**,是失敗判定的依據。
* 每 2 秒每個 Sentinel 通過 Master 節點的 channel 交換信息(Publish/Subscribe);
* 每 10 秒每個 Sentinel 會對 Master 和 Slave 執行`INFO`命令,這個任務主要達到兩個目的:
* 發現 Slave 節點;
* 確認主從關系。
Redis 哨兵機制如何實現故障自動轉移?
### sentinel 集群通過主觀下線和客觀下線判斷redis節點是否失效
默認情況下,每個 Sentinel 節點會以每秒一次的頻率對Redis 節點和其它的Sentinel 節點發送 PING 命令,并通過節點的 回復 來判斷節點是否在線。
* 主觀下線
主觀下線 適用于所有 主節點 和 從節點。如果在 down-after-milliseconds 毫秒內,Sentinel 沒有收到 目標節點 的有效回復,則會判定 該節點 為 主觀下線。
* 客觀下線
客觀下線 只適用于 主節點。如果 主節點 出現故障,Sentinel 節點會通過 sentinel is-master-down-by-addr 命令,向其它 Sentinel 節點詢問對該節點的 狀態判斷。如果超過 個數的節點判定 主節點 不可達,則該 Sentinel 節點會判斷 主節點為客觀下線。
當判斷某個Redis節點是客觀下線后,Sentinel會把master轉移到另外的slave節點,讓它充當新的master接受請求,從而保證高可用性。
### 優點
* 哨兵模式是基于主從模式的,所有主從的優點,哨兵模式都有;
* 主從可以自動切換,系統更健壯,可用性更高;
* Sentinel 會不斷地檢查你的主服務器和從服務器是否運作正常。當被監控的某個 Redis 服務器出現問題時, Sentinel 可以通過 API 向管理員或者其他應用程序發送通知。
### 缺點
* 主從切換需要時間,會丟失數據;
* 還是沒有解決主節點寫的壓力;
* 主節點的寫能力,存儲能力受到單機的限制;
* 動態擴容困難復雜,對于集群,容量達到上限時在線擴容會變得很復雜。
### 什么是腦裂
redis的主從模式下腦裂是指因為網絡問題,導致redis master節點跟redis slave節點和sentinel集群處于不同的網絡分區,此時因為sentinel集群無法感知到 master 的存在,就會將某一個 slave 節點提升為 master 節點。此時就存在兩個不同的 master節點,就像一個大腦分裂成了兩個。
集群腦裂問題中,如果客戶端還在基于原來的 master 節點繼續寫入數據,那么新的master 節點將無法同步這些數據,當網絡問題解決之后,sentinel 集群將原先的master節點降為 slave 節點,此時再從新的 master 中同步數據,將會造成大量的數據丟失。
### 腦裂的解決方案
min-slaves-to-write 3
min-slaves-max-lag 10
第一個參數表示至少 master 要有1個slave節點,才允許寫入
第二個參數表示 slave 連接到 master 的最大延遲時間
## Cluster集群模式
### 什么是 Cluster 集群
Redis 集群是一種分布式數據庫方案,集群通過分片(sharding)來進行數據管理(「分治思想」的一種實踐),并提供復制和故障轉移功能。
將數據劃分為 16384 的 slots,每個節點負責一部分槽位。槽位的信息存儲于每個節點中。
它是去中心化的,如圖所示,該集群有三個 Redis 節點組成,每個節點負責整個集群的一部分數據,每個節點負責的數據多少可能不一樣。

三個節點相互連接組成一個對等的集群,它們之間通過`Gossip`協議相互交換集群信息,最后每個節點都保存著其他節點的 slots 分配情況。
一般情況下,節點的負責的slot的數量是 16384/N 個,
還可以使用`cluster addslots`命令,指定每個實例上的哈希槽個數

### Cluster 如何實現故障轉移?
Redis 集群節點采用`Gossip`協議來廣播自己的狀態以及自己對整個集群認知的改變。比如一個節點發現某個節點失聯了 (PFail),它會將這條信息向整個集群廣播,其它節點也就可以收到這點失聯信息。
如果一個節點收到了某個節點失聯的數量 (PFail Count) 已經達到了集群的大多數,就可以標記該節點為確定下線狀態 (Fail),然后向整個集群廣播,強迫其它節點也接收該節點已經下線的事實,并立即對該失聯節點進行主從切換。
### 客戶端又怎么確定訪問的數據分布在哪個實例上呢?
Redis 實例會將自己的哈希槽信息通過 Gossip 協議發送給集群中其他的實例,實現了哈希槽分配信息的擴散。
這樣,集群中的每個實例都有所有哈希槽與實例之間的映射關系信息。
### 什么是 Redis 重定向機制?
哈希槽與實例之間的映射關系由于新增實例或者負載均衡重新分配導致改變了,**客戶端將請求發送到實例上,這個實例沒有相應的數據,該 Redis 實例會告訴客戶端將請求發送到其他的實例上**。
Redis 通過 MOVED 錯誤和 ASK 錯誤告訴客戶端。
> MOVED
**MOVED**錯誤(負載均衡,數據已經遷移到其他實例上):當客戶端將一個鍵值對操作請求發送給某個實例,而這個鍵所在的槽并非由自己負責的時候,該實例會返回一個 MOVED 錯誤指引轉向正在負責該槽的節點。
同時,**客戶端還會更新本地緩存,將該 slot 與 Redis 實例對應關系更新正確**。
> ASK
>
如果某個 slot 的數據比較多,部分遷移到新實例,還有一部分沒有遷移。
如果請求的 key 在當前節點找到就直接執行命令,否則時候就需要 ASK 錯誤響應了。
> ask 和move move是已經遷移完成 ask 是正在遷移
### Redis Cluster 模式的優缺點?
答案:實現了Redis的分布式存儲,即每臺節點存儲不同的內容,來解決在線擴容的問題。
1、優點:
* 無中心架構,數據按照slot分布在多個節點
* 集群中的每個節點都是平等的,每個節點都保存各自的數據和整個集群的狀態。每個節點都和其他所有節點連接,而且這些連接保持活躍,這樣就保證了我們只需要連接集群中的任意一個節點,就可以獲取到其他節點的數據。
* 可線性擴展到1000個節點,節點可動態添加或刪除
* 能夠實現自動故障轉移,節點之間通過`gossip協議`交換狀態信息,用投票機制完成slave到master的角色轉換
缺點:
* 數據通過異步復制,不保證數據的強一致性
* slave充當 “冷備”,不對外提供讀、寫服務,只作為故障轉移使用。
* 批量操作限制,目前只支持具有相同slot值的key執行批量操作,對mset、mget、sunion等操作支持不友好
* key事務操作支持有限,只支持多key在同一節點的事務操作,多key分布在不同節點時無法使用事務功能
* 不支持多數據庫空間,一臺redis可以支持16個db,集群模式下只能使用一個,即`db 0`。
Redis Cluster模式不建議使用pipeline和multi-keys操作,減少max redirect產生的場景。
### 增加和刪除節點
> 數據來源:https://www.yisu.com/zixun/146512.html
查看集群啟動情況:`ps -ef | grep redis`
查看集群的slots分配情況以及節點之間的主從關系:
首先登陸節點7000:redis-cli -p 7000 -h 192.168.182.132 -c //注意不要丟了-c 僅把7000節點當作命令行窗口
#### 增加
**eg:**
>1、準備新的節點
在集群目錄redis\_cluster目錄下增加redis7006和redis7007目錄
mkdir redis7006
mkdir redis7007

復制端口7000的redis.conf配置文件到redis7006和redis7007目錄下,并修改配置文件中的端口為對應目錄的端口號。
例如redis7006下的redis.conf文件的內容為:
```
~~~
port?7006
bind?192.168.182.132?//本機IP
daemonize?yes?//設置為后臺運行
pidfile?/var/run/redis-7006.pid
cluster-enabled?yes?//開啟集群
cluster-config-file?node-7006.conf
cluster-node-timeout?15000
appendonly?yes
~~~
```
>2、啟動兩個新的redis節點
```
redis-server?redis7006/redis.conf
redis-server?redis7007/redis.conf
ps?-ef?|?grep?redis?//查看新的redis節點是否啟動成功
```
登錄新的節點
~~~
redis-cli?-p?7006?-h?192.168.182.132?-c
cluster?nodes
~~~

>3、添加主節點
* 向集群中添加節點7006,注意一定要保證節點里面沒有添加過任何數據,不然添加會報錯
~~~
cd?/usr/local/redis/redis/src
./redis-trib.rb?add-node?192.168.182.132:7006?192.168.182.132:7000
?//第一次節點為新增的節點?第二個節點為集群中的節點
~~~
* 為新增的主節點增加slot
~~~
cd?/usr/local/redis/redis/src
./redis-trib.rb?reshard?192.168.182.132:7001?//可以為任意的節點?在此登錄的7001只是作為客戶端去訪問的
~~~

執行后:
因為我們增加7006為主節點后,一共存在四個主節點,為了平均分配我們需要給7006分配16384除以4等于4096個節點,所以我們輸入4096,按enter繼續:

輸入7006的節點ID,按enter繼續:

從哪些主節點抽取槽到新節點中:all為所有主節點,done:指定節點,在這里我們輸入all,按enter繼續:

輸入yes后按enter開始給7006分配虛擬槽,分配完成后
~~~
redis-cli?-p?7000?-h?192.168.182.132?-c
cluster?nodes
~~~

> 4、添加從節點7007
* 使用add-node添加新節點
~~~
cd?/usr/local/redis/redis/src
./redis-trib.rb?add-node?192.168.182.132:7007?192.168.182.132:7000
~~~

* 將7007變為7006的 從節點
~~~
redis-cli?-p?7007?-h?192.168.182.132
//后面的字符串為節點7006的節點ID
cluster?replicate?52d169e7011ccdf10f99c1d83f92409dcc37ab55
~~~
#### 刪除
刪除節點的話,要先刪除該節點的從節點,避免從節點頂替成主節點
只要使用del-node命令即可:
~~~
./redis-trib?del-node?127.0.0.1:7000?<node-id>
第一個參數是任意一個節點的地址,第二個節點是你想要移除的節點地址。
~~~
使用同樣的方法移除主節點,不過在移除主節點前,需要確保這個主節點是空的. 如果不是空的,需要將這個節點的數據重新分片到其他主節點上.
>1、刪除從節點
刪除節點用del-node命令。此命令需要制定刪除節點的ip和端口,以及節點的id。
~~~
cd?/usr/local/redis/redis/src
./redis-trib.rb?del-node?192.168.182.132:7007?7007節點ID
~~~
>2、刪除主節點
* 將主節點7006的slots分配到其他主節點上
~~~
cd?/usr/local/redis/redis/src
./redis-trib.rb?reshard?192.168.182.132:7006
~~~

此時是可以選擇要把slot轉移到具體的節點,或者是選擇all(代表所以節點)
* 使用del-node命令來刪除7006主節點。
~~~
cd?/usr/local/redis/redis/src
./redis-trib.rb?del-node?192.168.182.132:7006?52d169e7011ccdf10f99c1d83f92409dcc37ab55
~~~
- 消息隊列
- 為什么要用消息隊列
- 各種消息隊列產品的對比
- 消息隊列的優缺點
- 如何保證消息隊列的高可用
- 如何保證消息不丟失
- 如何保證消息不會重復消費?如何保證消息的冪等性?
- 如何保證消息消費的順序性?
- 基于MQ的分布式事務實現
- Beanstalk
- PHP
- 函數
- 基礎
- 基礎函數題
- OOP思想及原則
- MVC生命周期
- PHP7.X新特性
- PHP8新特性
- PHP垃圾回收機制
- php-fpm相關
- 高級
- 設計模式
- 排序算法
- 正則
- OOP代碼基礎
- PHP運行原理
- zavl
- 網絡協議new
- 一面
- TCP和UDP
- 常見狀態碼和代表的意義以及解決方式
- 網絡分層和各層有啥協議
- TCP
- http
- 二面
- TCP2
- DNS
- Mysql
- 鎖
- 索引
- 事務
- 高可用?高并發?集群?
- 其他
- 主從復制
- 主從復制數據延遲
- SQL的語?分類
- mysqlQuestions
- Redis
- redis-question
- redis為什么那么快
- redis的優缺點
- redis的數據類型和使用場景
- redis的數據持久化
- 過期策略和淘汰機制
- 緩存穿透、緩存擊穿、緩存雪崩
- redis的事務
- redis的主從復制
- redis集群架構的理解
- redis的事件模型
- redis的數據類型、編碼、數據結構
- Redis連接時的connect與pconnect的區別是什么?
- redis的分布式鎖
- 緩存一致性問題
- redis變慢的原因
- 集群情況下,節點較少時數據分布不均勻怎么辦?
- redis 和 memcached 的區別?
- 基本算法
- MysqlNew
- 索引new
- 事務new
- 鎖new
- 日志new
- 主從復制new
- 樹結構
- mysql其他問題
- 刪除
- 主從配置
- 五種IO模型
- Kafka
- Nginx
- trait
- genergtor 生成器
- 如何實現手機掃碼登錄功能
- laravel框架的生命周期