搭建Redis的三种集群
Redis支持三种集群方案
主从复制模式
Sentinel哨兵模式
Cluster模式
主从复制模式
工作原理:

优点:
读写分离,提高服务器性能
缺点:
一旦master节点不能提供服务,需要人工将Slave节点切换成master节点
主节点只能有一个,不能承受高并发
客户端无法感知到master节点的变化
搭建主从集群
通过docker-compose运行一个master节点和一个slave节点
docker-compose目录结构
.├── docker-compose.yml├── master│ ├── data│ │ └── dump.rdb│ └── redis.conf└── slave ├── data │ └── dump.rdb └── redis.confdocker-compose.yml
version: "3"networks: redis-replication: driver: bridge ipam: config: - subnet: 192.168.0.0/24services: master: image: redis container_name: redis-master ports: - 6379:6379 volumes: - "./master/redis.conf:/etc/redis/redis.conf" - "./master/data:/data" command: ["redis-server", "/etc/redis/redis.conf"] restart: always networks: redis-replication: ipv4_address: 192.168.0.2 slave: image: redis container_name: redis-slave ports: - 6380:6379 volumes: - "./slave/redis.conf:/etc/redis/redis.conf" - "./slave/data:/data" command: ["redis-server", "/etc/redis/redis.conf"] restart: always networks: redis-replication: ipv4_address: 192.168.0.3master/redis.conf
port 6379protected-mode no#【Master】Diskless 就是直接将要复制的 RDB 文件写入到 Socket 中,而不会先存储到磁盘上repl-diskless-sync no#【Master】是否开启 Nagle 算法,可以减少流量占用,但会同步得慢些repl-disable-tcp-nodelay noslave/redis.conf
port 6379protected-mode no#【Slave】连接 Master 的配置slaveof 192.168.0.2 6379#【Slave】只读模式slave-read-only yes#【Slave】复制期间是否允许响应查询,可能会返回脏数据slave-serve-stale-data yes启动
docker-compose up启动日志:
[+] Building 0.0s (0/0) docker:desktop-linux[+] Running 3/0 ✔ Network redis_redis-replication Created 0.0s ✔ Container redis-master Created 0.0s ✔ Container redis-slave Created 0.0sAttaching to redis-master, redis-slaveredis-master | 1:C 19 Nov 2023 09:27:02.640 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Ooredis-master | 1:C 19 Nov 2023 09:27:02.640 # Redis version=7.0.4, bits=64, commit=00000000, modified=0, pid=1, just startedredis-master | 1:C 19 Nov 2023 09:27:02.640 # Configuration loadedredis-master | 1:M 19 Nov 2023 09:27:02.640 * monotonic clock: POSIX clock_gettimeredis-master | 1:M 19 Nov 2023 09:27:02.641 * Running mode=standalone, port=6379.redis-master | 1:M 19 Nov 2023 09:27:02.641 # Server initializedredis-master | 1:M 19 Nov 2023 09:27:02.641 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.redis-master | 1:M 19 Nov 2023 09:27:02.643 * Loading RDB produced by version 7.0.4redis-master | 1:M 19 Nov 2023 09:27:02.643 * RDB age 3063 secondsredis-master | 1:M 19 Nov 2023 09:27:02.643 * RDB memory usage when created 0.97 Mbredis-master | 1:M 19 Nov 2023 09:27:02.643 * Done loading RDB, keys loaded: 0, keys expired: 0.redis-master | 1:M 19 Nov 2023 09:27:02.644 * DB loaded from disk: 0.001 secondsredis-master | 1:M 19 Nov 2023 09:27:02.644 * Ready to accept connectionsredis-slave | 1:C 19 Nov 2023 09:27:02.649 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Ooredis-slave | 1:C 19 Nov 2023 09:27:02.649 # Redis version=7.0.4, bits=64, commit=00000000, modified=0, pid=1, just startedredis-slave | 1:C 19 Nov 2023 09:27:02.649 # Configuration loadedredis-slave | 1:S 19 Nov 2023 09:27:02.649 * monotonic clock: POSIX clock_gettimeredis-slave | 1:S 19 Nov 2023 09:27:02.650 * Running mode=standalone, port=6379.redis-slave | 1:S 19 Nov 2023 09:27:02.650 # Server initializedredis-slave | 1:S 19 Nov 2023 09:27:02.650 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.redis-slave | 1:S 19 Nov 2023 09:27:02.652 * Loading RDB produced by version 7.0.4redis-slave | 1:S 19 Nov 2023 09:27:02.652 * RDB age 31 secondsredis-slave | 1:S 19 Nov 2023 09:27:02.652 * RDB memory usage when created 0.87 Mbredis-slave | 1:S 19 Nov 2023 09:27:02.652 * Done loading RDB, keys loaded: 0, keys expired: 0.redis-slave | 1:S 19 Nov 2023 09:27:02.652 * DB loaded from disk: 0.000 secondsredis-slave | 1:S 19 Nov 2023 09:27:02.652 * Before turning into a replica, using my own master parameters to synthesize a cached master: I may be able to synchronize with the new master with just a partial transfer.redis-slave | 1:S 19 Nov 2023 09:27:02.652 * Ready to accept connectionsredis-slave | 1:S 19 Nov 2023 09:27:02.652 * Connecting to MASTER 192.168.0.2:6379redis-slave | 1:S 19 Nov 2023 09:27:02.652 * MASTER <-> REPLICA sync startedredis-slave | 1:S 19 Nov 2023 09:27:02.652 * Non blocking connect for SYNC fired the event.redis-slave | 1:S 19 Nov 2023 09:27:02.652 * Master replied to PING, replication can continue...redis-slave | 1:S 19 Nov 2023 09:27:02.653 * Trying a partial resynchronization (request d9325e3543cdeb24055e06f3ed5623c7bc2c2c33:267).redis-master | 1:M 19 Nov 2023 09:27:02.653 * Replica 192.168.0.3:6379 asks for synchronizationredis-master | 1:M 19 Nov 2023 09:27:02.653 * Partial resynchronization request from 192.168.0.3:6379 accepted. Sending 0 bytes of backlog starting from offset 267.redis-slave | 1:S 19 Nov 2023 09:27:02.653 * Successful partial resynchronization with master.redis-slave | 1:S 19 Nov 2023 09:27:02.653 # Master replication ID changed to f4b77e85d3ef205932ee0d76d9c65f73b1d56466redis-slave | 1:S 19 Nov 2023 09:27:02.653 * MASTER <-> REPLICA sync: Master accepted a Partial Resynchronization.哨兵模式
优点:
对节点进行监控,自动完成故障转移
缺点:
主从切换存在瞬断情况,等待时间比较长
哨兵模式只有一个主节点对外提供服务,没办法支持高并发
哨兵之间的通信方式:
哨兵互相之间的通信,是通过redis的pub/sub系统实现的,每个哨兵都会往订阅通道的里发送消息,这时候所有其他哨兵都可以消费到这个消息,并感知到其他的哨兵的存在。每隔两秒钟,每个哨兵都会往自己监控的某个master+slaves对应的订阅通道里发送一个消息,内容是自己的host、ip和runid还有对这个master的监控配置。每个哨兵也会去监听自己监控的每个master+slaves对应的订阅通道,然后去感知到同样在监听这个master+slaves的其他哨兵的存在。每个哨兵还会跟其他哨兵交换对master的监控配置,互相进行监控配置的同步。
哨兵之间如何选出leader:
每一个sentinel节点都可以成为leader,当一个sentinel节点确认redis集群主节点主观下线之后,会请求其他sentinel节点选自己为leader,如果被请求的节点没有选过这个节点,则同意,否则不同意。当票数大于节点数量的一半时,该sentinel节点选举为leader。否则重新选举。(raft算法)
哨兵模式如何监控主从节点:
哨兵会每隔 1 秒给所有主从节点发送 PING 命令,当主从节点收到 PING 命令后,会发送一个响应命令给哨兵,这样就可以判断它们是否在正常运行。
哨兵模式如何选主节点:
先选出sentinel leader节点,然后再根据以下规则选出master节点
1.用户可以通过slave-priority配置项配置优先级,哨兵节点会将优先级更高、达到切换条件的从节点作为新的主节点
2.和旧主库同步程度最接近的节点(通过对比master_repl_offset和slave_repl_offset)
3.每一个实例都有一个ID,选择ID最小的从节点
搭建哨兵集群
在主从复制的基础上,增加一个sentinel配置
.├── docker-compose.yml├── master│ ├── data│ │ └── dump.rdb│ └── redis.conf├── sentinel│ └── sentinel.conf└── slave ├── data │ └── dump.rdb └── redis.confdocker-compose.yml
version: "3"networks: redis-replication: driver: bridge ipam: config: - subnet: 192.168.0.0/24services: master: image: redis container_name: redis-master ports: - 6379:6379 volumes: - "./master/redis.conf:/etc/redis/redis.conf" - "./master/data:/data" command: ["redis-server", "/etc/redis/redis.conf"] restart: always networks: redis-replication: ipv4_address: 192.168.0.2 slave-1: image: redis container_name: redis-slave-1 ports: - 6380:6379 volumes: - "./slave/redis.conf:/etc/redis/redis.conf" - "./slave/data:/data" command: ["redis-server", "/etc/redis/redis.conf"] restart: always networks: redis-replication: ipv4_address: 192.168.0.3 slave-2: image: redis container_name: redis-slave-2 ports: - 6381:6379 volumes: - "./slave/redis.conf:/etc/redis/redis.conf" - "./slave/data:/data" command: ["redis-server", "/etc/redis/redis.conf"] restart: always networks: redis-replication: ipv4_address: 192.168.0.4 sentinel: image: redis container_name: redis-sentinel ports: - 26379:26379 volumes: - "./sentinel/sentinel.conf:/etc/redis/sentinel.conf" command: ["redis-sentinel", "/etc/redis/sentinel.conf"] restart: always networks: redis-replication: ipv4_address: 192.168.0.100sentinel.conf
port 26379# 监视redis主节点,至少有1个sentinel认为主节点失效,才会进行故障转移sentinel monitor redis-master 192.168.0.2 6379 1# 5000毫秒内没有收到主节点的心跳,sentinel认为主节点失效sentinel down-after-milliseconds redis-master 5000# 指定故障转移的超时时间,超过该时间,故障转移失败sentinel failover-timeout redis-master 10000# 执行在故障转移过程中,slave节点数据同步的最大数量sentinel parallel-syncs redis-master 1SpringBootData连接redis Sentinel集群
application.yml
spring: redis: sentinel: master: redis-master nodes: - 127.0.0.1:26379RedisConfiguration.java
@Configurationpublic class RedisConfiguration { @Bean public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(lettuceConnectionFactory); // 设置key序列化方式string,RedisSerializer.string() 等价于 new StringRedisSerializer() redisTemplate.setKeySerializer(RedisSerializer.string()); // 设置value的序列化方式json,使用GenericJackson2JsonRedisSerializer替换默认序列化,RedisSerializer.json() 等价于 new GenericJackson2JsonRedisSerializer() redisTemplate.setValueSerializer(RedisSerializer.json()); // 设置hash的key的序列化方式 redisTemplate.setHashKeySerializer(RedisSerializer.string()); // 设置hash的value的序列化方式 redisTemplate.setHashValueSerializer(RedisSerializer.json()); // 使配置生效 redisTemplate.afterPropertiesSet(); return redisTemplate; }}踩坑
Lettuce客户端在哨兵模式下,不能自动切换主从节点!
Cluster模式
cluster模式既有高可用,也可以动态扩容,可以抗住高并发请求。
redis cluster如何将数据进行分片?
redis cluster将所有的key根据哈希函数映射到0~16383之间
Slot = CRC16(key)&16384
每个节点负责维护一部分哈希槽,例如如果你有3个节点,A节点存储0-5460,B节点存储5461-10922,C节点存储10923-16363

redis cluster如何扩容?
新增节点,然后使用redis-cli的rehash命令手动分配数据槽。
redis-cli --cluster reshard 192.168.0.100:6379redis集群扩容的过程中是集群否可用?
在 Redis 集群进行 rehash(重新分片)过程中,集群仍然是可用的。Redis 集群使用哈希槽(hash slot)来分片数据,每个节点负责一部分哈希槽。当需要进行 rehash 时,集群会将部分哈希槽从一个节点移动到另一个节点,以实现数据的重新分片。
