### 數據分布
分布式數據庫首先要解決把整個數據集按照分區規則映射到多個節點的問題,即把數據集劃分到多個節點上,每個節點負責整體數據的一個子集。
### 數據分布有兩種方式
#### 1 順序分區
順序分布就是把一整塊數據分散到很多機器中,如下圖所示。

正常順序分區是按照平均分配的規則,當然也可以根據不同機器分配,內存大一點的可以多分配一些。
#### 2.哈希分區。
如下圖所示,1~100這整塊數字,通過 hash 的函數,取余產生的數。這樣可以保證這串數字充分的打散,也保證了均勻的分配到各臺機器上。

哈希分布和順序分布只是場景上的適用。哈希分布不能順序訪問,比如你想訪問1~100,哈希分布只能遍歷全部數據,同時哈希分布因為做了 hash 后導致與業務數據無關了。

### 數據傾斜與數據遷移跟節點伸縮
順序分布是會導致數據傾斜的,主要是訪問的傾斜。每次點擊會重點訪問某臺機器,這就導致最后數據都到這臺機器上了,這就是順序分布最大的缺點。
但哈希分布其實是有個問題的,當我們要擴容機器的時候,專業上稱之為“節點伸縮”,這個時候,因為是哈希算法,會導致數據遷移。
### 哈希分區方式
#### 1、節點取余分區
使用特定的數據(包括redis的鍵或用戶ID),再根據節點數量N,使用公式:hash(key)%N計算出一個0~(N-1)值,用來決定數據映射到哪一個節點上。即哈希值對節點總數取余。余數x,表示這條數據存放在第(x+1)個節點上。
缺點:當節點數量N變化時(擴容或者收縮),數據和節點之間的映射關系需要重新計算,這樣的話,按照新的規則映射,要么之前存儲的數據找不到,要么之前數據被重新映射到新的節點(導致以前存儲的數據發生數據遷移)
實踐:常用于數據庫的分庫分表規則,一般采用預分區的方式,提前根據數據量規劃好分區數,比如劃分為512或1024張表,保證可支撐未來一段時間的數據量,再根據負載情況將表遷移到其他數據庫中。
#### 2、一致性哈希
一致性哈希分區(Distributed Hash Table)實現思路是為系統中每個節點分配一個 token,范圍一般在0~232,這些 token 構成一個哈希環。數據讀寫執行節點查找操作時,先根據 key 計算 hash 值,然后順時針找到第一個大于等于該哈希值的 token 節點

上圖就是一個一致性哈希的原理解析
**圖片來自**[什么是一致性哈希](https://www.jianshu.com/p/49e3fbf41b9b)。
假設我們有 n1~n4 這四臺機器,我們對每一臺機器分配一個唯一 token,每次有數據(圖中黃色代表數據),一致性哈希算法規定每次都順時針漂移數據,也就是圖中黃色的數 據都指向 n3。
這個時候我們需要增加一個節點 n5,在 n2 和 n3 之間,數據還是會發生漂移(會偏移到大于等于的節點),但是這個時候你是否注意到,其實只有 n2~n3 這部分的數據被漂移,其他的數據都是不會變的,這種方式相比節點取余最大的好處在于加入和刪除節點只影響哈希環中相鄰的節點,對其他節點無影響
缺點:每個節點的負載不相同,因為每個節點的hash是根據key計算出來的,換句話說就是假設key足夠多,被hash算法打散得非常均勻,但是節點過少,導致每個節點處理的key個數不太一樣,甚至相差很大,這就會導致某些節點壓力很大
實踐:加減節點會造成哈希環中部分數據無法命中,需要手動處理或者忽略這部分數據,因此一致性哈希常用于緩存場景。
#### 3.虛擬槽分區
虛擬槽分區巧妙地使用了哈希空間,使用分散度良好的哈希函數把所有數據映射到一個固定范圍的整數集合中,整數定義為槽(slot)。這個范圍一般遠遠大于節點數,比如 Redis Cluster 槽范圍是0~16383。槽是集群內數據管理和遷移的基本單位。采用大范圍槽的主要目的是為了方便數據拆分和集群擴展。每個節點會負責一定數量的槽,下圖所示。
當前集群有5個節點,每個節點平均大約負責3276個槽。由于采用高質量的哈希算法,每個槽所映射的數據通常比較均勻,將數據平均劃分到5個節點進行數據分區。Redis Cluster 就是采用虛擬槽分區,下面就介紹 Redis 數據分區方法。

每當 key 訪問過來,Redis Cluster 會計算哈希值是否在這個區間里。它們彼此都知道對應的槽在哪臺機器上,這樣就能做到平均分配了。
### redis-cluster的優勢
#### 性能:
這是Redis賴以生存的看家本領,增加集群功能后當然不能對性能產生太大影響,所以Redis采取了P2P而非Proxy方式、異步復制、客戶端重定向等設計,而犧牲了部分的一致性、使用性。
#### 水平擴展:
集群的最重要能力當然是擴展,文檔中稱可以線性擴展到1000結點。
#### 可用性:
在Cluster推出之前,可用性要靠Sentinel保證。有了集群之后也自動具有了Sentinel的監控和自動Failover能力。
### 搭建集群
#### 1、準備節點
Redis 集群一般由多個節點組成,節點數量至少為6個才能保證組成完整高可用的集群。每個節點需要開啟配置 cluster-enabled yes,讓 Redis 運行在集群模式下,上面的配置都相應的給到redis的配置文件當中并啟動。
其他配置和單機模式一致即可,配置文件命名規則 redis-{port}.conf,準備好配置后啟動所有節點,第一次啟動時如果沒有集群配置文件,它會自動創建一份,文件名稱采用 cluster-config-file 參數項控制,建議采用 node-{port}.conf 格式定義,也就是說會有兩份配置文件
當集群內節點信息發生變化,如添加節點、節點下線、故障轉移等。節點會自動保存集群狀態到配置文件中。需要注意的是,Redis 自動維護集群配置文件,不要手動修改,防止節點重啟時產生集群信息錯亂
每個節點需要開啟配置 `cluster-enabled yes`,讓 Redis 運行在集群模式下。集群相關配置如下:
#節點端口
port 6379
#開啟集群模式
cluster-enabled yes
#節點超時時間,單位毫秒
cluster-node-timeout 15000
#集群內部配置文件
cluster-config-file "nodes-6379.conf"
配置文件信息如下:

文件內容記錄了集群初始狀態,這里最重要的是節點 ID,它是一個40位16進制字符串,用于唯一標識集群內一個節點,節點 ID 在集群初始化時只創建一次,節點重啟時會加載集群配置文件進行重用,結合做相應的集群操作,而 Redis 的運行 ID 每次重啟都會變化。
#### 2.節點握手
節點握手是指一批運行在集群模式下的節點通過 Gossip 協議彼此通信,達到感知對方的過程。節點握手是集群彼此通信的第一步,由客戶端發起命令:`cluster meet{ip}{port}`
關于Gossip可以看看[文章的介紹](http://www.10tiao.com/html/681/201803/2651029463/1.html)
通過命令 `cluster meet 127.0.0.1 6380`讓節點6379和6380節點進行握手通信。cluster meet 命令是一個異步命令,執行之后立刻返回。內部發起與目標節點進行握手通信
1)節點6379本地創建6380節點信息對象,并發送 meet 消息。
2)節點6380接受到 meet 消息后,保存6379節點信息并回復 pong 消息。
3)之后節點6379和6380彼此定期通過 `ping/pong` 消息進行正常的節點通信。
通過cluster nodes 命令確認6個節點都彼此感知并組成集群

注意:
1、每個Redis Cluster節點會占用兩個TCP端口,一個監聽客戶端的請求,默認是6379,另外一個在前一個端口加上10000,比如16379,來監聽數據的請求,節點和節點之間會監聽第二個端口,用一套二進制協議來通信。
節點之間會通過套協議來進行失敗檢測,配置更新,failover認證等等。
為了保證節點之間正常的訪問,需要注意防火墻的配置。
2、節點建立握手之后集群還不能正常工作,這時集群處于下線狀態,所有的數據讀寫都被禁止
### 3.設置從節點
作為一個完整的集群,需要主從節點,保證當它出現故障時可以自動進行故障轉移。集群模式下,Reids 節點角色分為主節點和從節點。
首次啟動的節點和被分配槽的節點都是主節點,從節點負責復制主節點槽信息和相關的數據。
使用 `cluster replicate {nodeId}`命令讓一個節點成為從節點。其中命令執行必須在對應的從節點上執行,將當前節點設置為 node_id 指定的節點的從節點
redis-cli -h 47.98.147.49 -p 6395 CLUSTER REPLICATE 0affa79edef47e10a0459832d279fe74467be98b
redis-cli -h 47.98.147.49 -p 6396 CLUSTER REPLICATE 1e30c186681638411f25f949f1b6ffded5f5d0a3
### 4.分配槽
Redis 集群把所有的數據映射到16384個槽中。每個 key 會映射為一個固定的槽,只有當節點分配了槽,才能響應和這些槽關聯的鍵命令。通過 `cluster addslots` 命令為節點分配槽
利用 bash 特性批量設置槽(slots),命令如下
redis-cli -h 47.98.147.49 -p 6391 cluster addslots {0..5461}
redis-cli -h 47.98.147.49 -p 6392 cluster addslots {5462..10922}
redis-cli -h 47.98.147.49 -p 6393 cluster addslots {10923..16383}
我們依照 Redis 協議手動建立一個集群。它由6個節點構成,3個主節點負責處理槽和相關數據,3個從節點負責故障轉移。手動搭建集群便于理解集群建立的流程和細節,但是我們從中發現集群搭建需要很多步驟,當集群節點眾多時,必然會加大搭建集群的復雜度和運維成本。**因此 Redis 官方提供了 redis-trib.rb 工具方便我們快速搭建集群。**
4.操作集群
-c 集群模式

如果沒有指定集群模式,那么會出現如下錯誤

所有命令:
`CLUSTER info`:打印集群的信息。
`CLUSTER nodes`:列出集群當前已知的所有節點(node)的相關信息。
`CLUSTER meet <ip> <port>`:將ip和port所指定的節點添加到集群當中。
`CLUSTER addslots <slot> [slot ...]`:將一個或多個槽(slot)指派(assign)給當前節點。
`CLUSTER delslots <slot> [slot ...]`:移除一個或多個槽對當前節點的指派。
`CLUSTER slots`:列出槽位、節點信息。
`CLUSTER slaves <node_id>`:列出指定節點下面的從節點信息。
`CLUSTER replicate <node_id>`:將當前節點設置為指定節點的從節點。
`CLUSTER saveconfig`:手動執行命令保存保存集群的配置文件,集群默認在配置修改的時候會自動保存配置文件。
`CLUSTER keyslot <key>`:列出key被放置在哪個槽上。
`CLUSTER flushslots`:移除指派給當前節點的所有槽,讓當前節點變成一個沒有指派任何槽的節點。
`CLUSTER countkeysinslot <slot>`:返回槽目前包含的鍵值對數量。
`CLUSTER getkeysinslot <slot> <count>`:返回count個槽中的鍵。
`CLUSTER setslot <slot> node <node_id>` :將槽指派給指定的節點,如果槽已經指派給另一個節點,那么先讓另一個節點刪除該槽,然后再進行指派。
`CLUSTER setslot <slot> migrating <node_id>` :將本節點的槽遷移到指定的節點中。
`CLUSTER setslot <slot> importing <node_id>` :從 node_id 指定的節點中導入槽 slot 到本節點。
`CLUSTER setslot <slot> stable` :取消對槽 slot 的導入(import)或者遷移(migrate)。
`CLUSTER failover`:手動進行故障轉移。
`CLUSTER forget <node_id>`:從集群中移除指定的節點,這樣就無法完成握手,過期時為60s,60s后兩節點又會繼續完成握手。
`CLUSTER reset [HARD|SOFT]`:重置集群信息,soft是清空其他節點的信息,但不修改自己的id,hard還會修改自己的id,不傳該參數則使用soft方式。
`CLUSTER count-failure-reports <node_id>`:列出某個節點的故障報告的長度。
`CLUSTER SET-CONFIG-EPOCH`:設置節點epoch,只有在節點加入集群前才能設置。
- 微服務
- 服務器相關
- 操作系統
- 極客時間操作系統實戰筆記
- 01 程序的運行過程:從代碼到機器運行
- 02 幾行匯編幾行C:實現一個最簡單的內核
- 03 黑盒之中有什么:內核結構與設計
- Rust
- 入門:Rust開發一個簡單的web服務器
- Rust的引用和租借
- 函數與函數指針
- Rust中如何面向對象編程
- 構建單線程web服務器
- 在服務器中增加線程池提高吞吐
- Java
- 并發編程
- 并發基礎
- 1.創建并啟動線程
- 2.java線程生命周期以及start源碼剖析
- 3.采用多線程模擬銀行排隊叫號
- 4.Runnable接口存在的必要性
- 5.策略模式在Thread和Runnable中的應用分析
- 6.Daemon線程的創建以及使用場景分析
- 7.線程ID,優先級
- 8.Thread的join方法
- 9.Thread中斷Interrupt方法學習&采用優雅的方式結束線程生命周期
- 10.編寫ThreadService實現暴力結束線程
- 11.線程同步問題以及synchronized的引入
- 12.同步代碼塊以及同步方法之間的區別和關系
- 13.通過實驗分析This鎖和Class鎖的存在
- 14.多線程死鎖分析以及案例介紹
- 15.線程間通信快速入門,使用wait和notify進行線程間的數據通信
- 16.多Product多Consumer之間的通訊導致出現程序假死的原因分析
- 17.使用notifyAll完善多線程下的生產者消費者模型
- 18.wait和sleep的本質區別
- 19.完善數據采集程序
- 20.如何實現一個自己的顯式鎖Lock
- 21.addShutdownHook給你的程序注入鉤子
- 22.如何捕獲線程運行期間的異常
- 23.ThreadGroup API介紹
- 24.線程池原理與自定義線程池一
- 25.給線程池增加拒絕策略以及停止方法
- 26.給線程池增加自動擴充,閑時自動回收線程的功能
- JVM
- C&C++
- GDB調試工具筆記
- C&C++基礎
- 一個例子理解C語言數據類型的本質
- 字節順序-大小端模式
- Php
- Php源碼閱讀筆記
- Swoole相關
- Swoole基礎
- php的五種運行模式
- FPM模式的生命周期
- OSI網絡七層圖片速查
- IP/TCP/UPD/HTTP
- swoole源代碼編譯安裝
- 安全相關
- MySql
- Mysql基礎
- 1.事務與鎖
- 2.事務隔離級別與IO的關系
- 3.mysql鎖機制與結構
- 4.mysql結構與sql執行
- 5.mysql物理文件
- 6.mysql性能問題
- Docker&K8s
- Docker安裝java8
- Redis
- 分布式部署相關
- Redis的主從復制
- Redis的哨兵
- redis-Cluster分區方案&應用場景
- redis-Cluster哈希虛擬槽&簡單搭建
- redis-Cluster redis-trib.rb 搭建&原理
- redis-Cluster集群的伸縮調優
- 源碼閱讀筆記
- Mq
- ELK
- ElasticSearch
- Logstash
- Kibana
- 一些好玩的東西
- 一次折騰了幾天的大華攝像頭調試經歷
- 搬磚實用代碼
- python讀取excel拼接sql
- mysql大批量插入數據四種方法
- composer好用的鏡像源
- ab
- 環境搭建與配置
- face_recognition本地調試筆記
- 虛擬機配置靜態ip
- Centos7 Init Shell
- 發布自己的Composer包
- git推送一直失敗怎么辦
- Beyond Compare過期解決辦法
- 我的Navicat for Mysql
- 小錯誤解決辦法
- CLoin報錯CreateProcess error=216
- mysql error You must reset your password using ALTER USER statement before executing this statement.
- VM無法連接到虛擬機
- Jetbrains相關
- IntelliJ IDEA 筆記
- CLoin的配置與使用
- PhpStormDocker環境下配置Xdebug
- PhpStorm advanced metadata
- PhpStorm PHP_CodeSniffer