概述
Redis作為基於鍵值對的NoSQL數據庫,具有高性能、豐富的數據結構、持久化、高可用、分布式等特性,同時Redis本身非常穩定,已經得到業界的廣泛認可和使用。
在Redis中,集群的解決方案有三種
- 主從復制
- 哨兵機制
- Cluster
Redis Cluster是Redis的分布式解決方案,在 3.0 版本正式推出。
集群方案的對比
1. 主從復制
同Mysql主從復制的原因一樣,Redis雖然讀取寫入的速度都特別快,但是也會產生讀壓力特別大的情況。為了分擔讀壓力,Redis支持主從復制,讀寫分離。一個Master可以有多個Slaves。
優點
- 數據備份
- 讀寫分離,提高服務器性能
缺點
- 不能自動故障恢復,RedisHA系統(需要開發)
- 無法實現動態擴容
2. 哨兵機制
Redis Sentinel是社區版本推出的原生高可用
解決方案,其部署架構主要包括兩部分:Redis Sentinel集群和Redis數據集群。
其中Redis Sentinel集群是由若干Sentinel節點組成的分布式集群,可以實現故障發現、故障自動轉移、配置中心和客戶端通知。Redis Sentinel的節點數量要滿足2n+1(n>=1)的奇數個。
優點
- 自動化故障恢復
缺點
- Redis 數據節點中 slave 節點作為備份節點不提供服務
- 無法實現動態擴容
3. Redis-Cluster
Redis Cluster是社區版推出的Redis分布式集群解決方案,主要解決Redis分布式方面的需求,比如,當遇到單機內存,並發和流量等瓶頸的時候,Redis Cluster能起到很好的負載均衡的目的。
Redis Cluster着眼於提高並發量
。
群集至少需要3主3從,且每個實例使用不同的配置文件。
在redis-cluster架構中,redis-master節點一般用於接收讀寫,而redis-slave節點則一般只用於備份
, 其與對應的master擁有相同的slot集合,若某個redis-master意外失效,則再將其對應的slave進行升級為臨時redis-master。
在redis的官方文檔中,對redis-cluster架構上,有這樣的說明:在cluster架構下,默認的,一般redis-master用於接收讀寫,而redis-slave則用於備份,當有請求是在向slave發起時,會直接重定向到對應key所在的master來處理
。 但如果不介意讀取的是redis-cluster中有可能過期的數據並且對寫請求不感興趣時,則亦可通過readonly
命令,將slave設置成可讀,然后通過slave獲取相關的key,達到讀寫分離。具體可以參閱redis官方文檔等相關內容
優點
- 解決分布式負載均衡的問題。具體解決方案是分片/虛擬槽slot。
- 可實現動態擴容
- P2P模式,無中心化
缺點
- 為了性能提升,客戶端需要緩存路由表信息
- Slave在集群中充當“冷備”,不能緩解讀壓力
網絡規划
這里沒有搭建虛擬機環境,全部在本地部署。本機的ip為 192.168.124.5
ip | port |
---|---|
192.168.124.5 | 7001 |
192.168.124.5 | 7002 |
192.168.124.5 | 7003 |
192.168.124.5 | 7004 |
192.168.124.5 | 7005 |
192.168.124.5 | 7006 |
Redis配置文件
在docker環境中,配置文件映射宿主機的時候,(宿主機)必須有配置文件。附件在這里。大家可以根據自己的需求定制配置文件。
下邊是我的配置文件 redis-cluster.tmpl
# redis端口
port ${PORT}
# 關閉保護模式
protected-mode no
# 開啟集群
cluster-enabled yes
# 集群節點配置
cluster-config-file nodes.conf
# 超時
cluster-node-timeout 5000
# 集群節點IP host模式為宿主機IP
cluster-announce-ip 192.168.124.5
# 集群節點端口 7001 - 7006
cluster-announce-port ${PORT}
cluster-announce-bus-port 1${PORT}
# 開啟 appendonly 備份模式
appendonly yes
# 每秒鍾備份
appendfsync everysec
# 對aof文件進行壓縮時,是否執行同步操作
no-appendfsync-on-rewrite no
# 當目前aof文件大小超過上一次重寫時的aof文件大小的100%時會再次進行重寫
auto-aof-rewrite-percentage 100
# 重寫前AOF文件的大小最小值 默認 64mb
auto-aof-rewrite-min-size 64mb
由於節點IP相同,只有端口上的差別,現在通過腳本 redis-cluster-config.sh
批量生成配置文件
for port in `seq 7001 7006`; do \ mkdir -p ./redis-cluster/${port}/conf \ && PORT=${port} envsubst < ./redis-cluster.tmpl > ./redis-cluster/${port}/conf/redis.conf \ && mkdir -p ./redis-cluster/${port}/data; \ done
生成的配置文件如下圖
Docker環境搭建
這里還是通過docker-compose進行測試環境的docker編排。
version: '3.7' services: redis7001: image: 'redis' container_name: redis7001 command: ["redis-server", "/usr/local/etc/redis/redis.conf"] volumes: - ./redis-cluster/7001/conf/redis.conf:/usr/local/etc/redis/redis.conf - ./redis-cluster/7001/data:/data ports: - "7001:7001" - "17001:17001" environment: # 設置時區為上海,否則時間會有問題 - TZ=Asia/Shanghai redis7002: image: 'redis' container_name: redis7002 command: ["redis-server", "/usr/local/etc/redis/redis.conf"] volumes: - ./redis-cluster/7002/conf/redis.conf:/usr/local/etc/redis/redis.conf - ./redis-cluster/7002/data:/data ports: - "7002:7002" - "17002:17002" environment: # 設置時區為上海,否則時間會有問題 - TZ=Asia/Shanghai redis7003: image: 'redis' container_name: redis7003 command: ["redis-server", "/usr/local/etc/redis/redis.conf"] volumes: - ./redis-cluster/7003/conf/redis.conf:/usr/local/etc/redis/redis.conf - ./redis-cluster/7003/data:/data ports: - "7003:7003" - "17003:17003" environment: # 設置時區為上海,否則時間會有問題 - TZ=Asia/Shanghai redis7004: image: 'redis' container_name: redis7004 command: ["redis-server", "/usr/local/etc/redis/redis.conf"] volumes: - ./redis-cluster/7004/conf/redis.conf:/usr/local/etc/redis/redis.conf - ./redis-cluster/7004/data:/data ports: - "7004:7004" - "17004:17004" environment: # 設置時區為上海,否則時間會有問題 - TZ=Asia/Shanghai redis7005: image: 'redis' container_name: redis7005 command: ["redis-server", "/usr/local/etc/redis/redis.conf"] volumes: - ./redis-cluster/7005/conf/redis.conf:/usr/local/etc/redis/redis.conf - ./redis-cluster/7005/data:/data ports: - "7005:7005" - "17005:17005" environment: # 設置時區為上海,否則時間會有問題 - TZ=Asia/Shanghai redis7006: image: 'redis' container_name: redis7006 command: ["redis-server", "/usr/local/etc/redis/redis.conf"] volumes: - ./redis-cluster/7006/conf/redis.conf:/usr/local/etc/redis/redis.conf - ./redis-cluster/7006/data:/data ports: - "7006:7006" - "17006:17006" environment: # 設置時區為上海,否則時間會有問題 - TZ=Asia/Shanghai
啟動結果如圖
集群配置
redis集群官方提供了配置腳本,4.x和5.x略有不同,具體可參見集群配置
下邊是我自己的環境
docker exec -it redis7001 redis-cli -p 7001 -a 123456 --cluster create 192.168.124.5:7001 192.168.124.5:7002 192.168.124.5:7003 192.168.124.5:7004 192.168.124.5:7005 192.168.124.5:7006 --cluster-replicas 1
看到如下結果說明集群配置成功
集群測試
接下來進行一些集群的基本測試
1. 查看集群通信是否正常
redis7001主節點對它的副本節點redis7005進行ping操作。
-h host -p port -a pwd
➜ docker docker exec -it redis7001 redis-cli -h 192.168.124.5 -p 7005 -a 123456 ping Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe. PONG
2. 測試簡單存儲
redis7001主節點客戶端操作redis7003主節點
➜ docker docker exec -it redis7001 redis-cli -h 192.168.124.5 -p 7003 -a 123456 Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe. 192.168.124.5:7003> set name admin (error) MOVED 5798 192.168.124.5:7002
由於Redis Cluster會根據key進行hash運算,然后將key分散到不同slots,name的hash運算結果在redis7002節點上的slots中。所以我們操作redis7003寫操作會自動路由到7002。然而error提示無法路由?沒關系,差一個 -c
參數而已。
再次運行查看結果如下:
➜ docker docker exec -it redis7001 redis-cli -h 192.168.124.5 -p 7003 -a 123456 -c Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe. 192.168.124.5:7003> set name admin -> Redirected to slot [5798] located at 192.168.124.5:7002 OK 192.168.124.5:7002> get name "admin" 192.168.124.5:7002>
3. 查看集群狀態
4. 查看slots分片
5. 查看集群信息
6. 測試讀寫分離
試試看,發現讀不到,原來在redis cluster中,如果你要在slave讀取數據,那么需要帶先執行 readonly
指令,然后 get key
7. 簡單壓測
選項 | 描述 |
---|---|
-t | 指定命令 |
-c | 客戶端連接數 |
-n | 總請求數 |
-d | set、get的value大小(單位byte) |
測試如下
➜ docker docker exec -it redis7001 bash root@cbc6e76a3ed2:/data# redis-benchmark -h 192.168.124.5 -p 7001 -t set -c 100 -n 50000 -d 20 ====== SET ====== 50000 requests completed in 10.65 seconds 100 parallel clients 20 bytes payload keep alive: 1 0.00% <= 2 milliseconds 0.01% <= 3 milliseconds ... 100.00% <= 48 milliseconds 100.00% <= 49 milliseconds 4692.63 requests per second
這里沒啥實際意義,在工作業務上大家可以根據QPS和主機配置進行壓測,計算規划出節點數量。
容災演練
現在我們殺掉主節點redis7001,看從節點redis7005是否會接替它的位置。
docker stop redis7001
再試着啟動7001,它將自動作為slave掛載到7005
SpringBoot配置Redis集群
在SpringBoot2.x版本中,redis默認的連接池已經更換為Lettuce,而不再是jedis。
- 在pom.xml中引入相關依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency>
- application.yml
spring: redis: timeout: 6000 password: 123456 cluster: max-redirects: 3 # 獲取失敗 最大重定向次數 nodes: - 192.168.124.5:7001 - 192.168.124.5:7002 - 192.168.124.5:7003 - 192.168.124.5:7004 - 192.168.124.5:7005 - 192.168.124.5:7006 lettuce: pool: max-active: 1000 #連接池最大連接數(使用負值表示沒有限制) max-idle: 10 # 連接池中的最大空閑連接 min-idle: 5 # 連接池中的最小空閑連接 max-wait: -1 # 連接池最大阻塞等待時間(使用負值表示沒有限制) cache: jcache: config: classpath:ehcache.xml
- redis配置
@Configuration @AutoConfigureAfter(RedisAutoConfiguration.class) public class RedisConfig { @Bean public RedisTemplate<String, Object> redisCacheTemplate(LettuceConnectionFactory redisConnectionFactory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); template.setConnectionFactory(redisConnectionFactory); return template; } }
- 基本測試
@SpringBootTest public class RedisTest { @Autowired private RedisTemplate<String, String> redisTemplate; @Test public void test() { redisTemplate.opsForValue().set("name", "admin"); String name = redisTemplate.opsForValue().get("name"); System.out.println(name); //輸出admin } }
總結
通過以上演示,基本上可以在本地環境下用我們的Redis Cluster集群了。最后再上一張本地映射文件的最終樣子,幫助大家了解Redis持久化及集群相關的東西。感興趣的小伙伴可以自行測試並查看其中的內容。