redis集群

单点redis问题

  • 数据丢失,实现redis持久化
  • 并发能力,主从集群,读写分离
  • 故障恢复,哨兵机制,健康监测,自动恢复
  • 存储能力,分片集群,插槽机制实现动态扩容

redis持久化

rdb,redis数据备份文件,实现持久化。
save命令开启,主进程阻塞完成。bgsave后台完成。停机时自动执行rdb。
redis.conf可以配置,save 900 1表示900秒内,如果至少一个key被修改,则执行bgsave,如果是save “”,则表示禁用rdb。dbfilename和dir也可以配置文件名和目录,rdbcompression可以配置是否压缩,但会消耗cpu。


bgsave开始时会fork主进程得到子进程,子进程共享主进程的内存数据,完成fork后读取内存数据写入rgb文件。fork采用copy-on-write技术,读操作访问共享内存,写操作拷贝数据执行写操作,所以极端情况所有数据都被修改了,可能内存会翻倍。

rdb缺点,执行间隔长,两次rdb之间写入数据有数据丢失的风险。fork子进程、压缩、写出rdb文件都比较耗时。

aof追加文件,redis处理的每一个写命令都会记录在aof文件,默认关闭,appendonly yes打开,appendname设置名字,频率设置,appendfsync always或者everysec或者no,no由操作系统决定何时刷缓存数据到磁盘,一般设置每秒刷盘。

bgrewriteaof解决文件体积过大问题,重写会创建一个当前AOF文件的体积优化版本。auto-aof-rewrite-percentage 100增长超过多少百分比自动触发,auto-aof-rewrite-min-size 64mb体积最小多大才可以触发重写。

单节点Rdis的并发能力是有上限的,要进一步提高Redis的并发能力,需要搭建主从集群,实现读写分离。

slaveof masterip masterport开启主从模式,info replication查看。

主从第一次同步是全量同步:

数据同步原理:replication id是数据集的标志,id一致代表是同一个数据集。每个master都有唯一的replid,slave会继承master节点的replid。offset偏移量,随着记录在repl_baklog中的数据增多而增大,slave完成同步时也会记录当前的offset,如果小于master的offset,说明slave落后于master,需要更新。因此slave做同步时,必须向master声明自己的replid(在成为slave前,自己也是master,所以是有自己的replid的)和offset,master才可以判断到底需要同步哪些数据。实际过程是先尝试做增量同步,发现id对不上,才开始全量同步。

主从第一次同步是全量同步,但如果slave重启后同步,则执行增量同步:

repl_baklog大小有上限,写满后会覆盖最早的数据。如果slave断开时间过久,致尚未备份的数据被覆盖,则无法基于log做增量同步,只能再次全量同步。

可以从以下几个方面来优化Redis主从就集群:

  • 在master中配置repl-diskless-sync yes启用无磁盘复制,避免全量同步时的磁盘I0。
  • Redis单节点上的内存占用不要太大,减少RDB导致的过多磁盘I0
  • 适当提高repl_baklog的大小,发现slave宕机时尽快实现故障恢复,尽可能避免全量同步
  • 限制一个master上的slave节点数量,如果实在是太多slave,则可以采用主﹣从﹣从链式结构,减少master压力


Redis提供了哨兵(Sentinel)机制来实现主从集群的自动故障恢复。
哨兵的结构和作用如下:

  • 监控:Sentinel会不断检查您的master和slave是否按预期工作
  • 自动故障恢复:如果master故障,Sentinel会将一个slave提升为master。当故障实例恢复后也以新的master为主
  • 通知:Sentinel充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给Redis的客户端


Sentinel基于心跳机制监测服务状态,每隔1秒向集群的每个实例发送ping命令:

  • 主观下线:如果某sentinel节点发现某实例未在规定时间响应,则认为该实例主观下线。
  • 客观下线:若超过指定数量(quorum)的sentinel都认为该实例主观下线,则该实例客观下线。quorum值最好超过Sentinel实例数量的一半

一旦发现master故障,sentinel需要在salve中选择一个作为新的master,选择依据是这样的:

  • 首先会判断slave节点与master节点断开时间长短,如果超过指定值(down-after-milliseconds *10)则会排除该slave节点
  • 然后判断slave节点的slave-priority值,越小优先级越高,如果是0则永不参与选举
  • 如果slave-prority一样,则判断slave节点的offset值,越大说明数据越新,优先级越高
  • 最后是判断slave节点的运行id大小,越小优先级越高,相当于随机。


当选中了其中一个slave为新的master后(例如slave1),故障的转移的步骤如下:

  • sentinel给备选的slave1节点发送slaveof no one 命令,让该节点成为master
  • sentinel给所有其它slave发送slaveof 192.168.150.101 7002命令,让这些slave成为新master的从节点,开始从新的master上同步数据。
  • 最后,sentinel将故障节点标记为slave,当故障节点恢复后会自动成为新的master的slave节点,这里直接修改的配置文件

sentinel集群搭建配置文件:

1
2
3
4
5
6
port 27001
sentinel announce-ip 152.168.153.161
sentinel monitor mymaster 192.168.150.10170012
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
dir "/tmp/s1"

解读:
port 27001:是当前 isentinel 实例的端口
sentinel monitor mymaster 192.158.150.101 7001 2:指定主节点信息
mymasterl :主节点名称,自定义,任意写
192.168.150.1017001:主节点的 ip 和端口
2:选举 master 时的 quorum 值

用户客户端连接sentinel集群就行,连接后订阅redis的集群信息,并与redis所有节点建立连接,读请求优先和slave联系,写请求则发到master。

主从和哨兵可以解决高可用、高并发读的问题。但是依然有两个问题没有解决:

  • 海量数据存储问题
  • 高并发写的问题

使用分片集群可以解决上述问题,分片集群特征:

  • 集群中有多个 master ,每个 master 保存不同数据.每个 master 都可以有多个 slave 节点
  • master 之间通过 ping 监测彼此健康状态
  • 客户端请求可以访问集群任意节点,最终都会被转发到正确节点


分片集群搭建配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#开启集群功能
cluster-enabled yes
#集群的配置文件名称,不需要我们创建,由redis自己维护
cluster-config-file /tmp/6379/nodes.conf
#节点心跳失败的超时时间
cluster-node-timeout 5000
#持久化文件存放目录
dir /tmp/6379
#绑定地址
bind 0.0.0.0
#让redis后台运行
daemonize yes
#注册的实例ip
replica-announce-ip 192.168.150.101
#保护模式
protected-mode no
#数据库数量
databases 1
#日志
logfile /tmp/6379/run.log

我们使用的是Redis6.2.4版本,集群管理以及集成到了redis-cli中,格式如下:

1
redis-cli --cluster create --cluster-replicas 1 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

命令说明:
redis-cli –cluster 或者./redis-trib.rb:代表集群操作命令
create :代表是创建集群
–cluster-replicas 1或者–replicas 1:指定集群中每个master的副本个数为1,此时节点总数/(replicas+1)得到的就是master的数量。因此节点列表中的前n个就是master,其它节点都是slave节点,随机分配到不同master。

redis-cli -p 7001 cluster nodes查看集群状态。集群模式命令操作要加-c,访问集群中的任意一个节点计算slot,最后redirect到对应slot的节点,减少跳转可以使用相同的{}作为key。

Redis会把每一个master(slave没有)节点映射到0~16383共16384个插槽(hash slot)上,查看集群信息时就能看到.

数据key不是与节点绑定,而是与插槽绑定。redis会根据key的有效部分计算插槽值,分两种情况:

  • key中包含”{}”,且”{}”中至少包含1个字符,”{}”中的部分是有效部分
  • key中不包含”{}”,整个key都是有效部分
    例如:key是num,那么就根据num计算,如果是{itcast}num,则根据itcast计算。计算方式是利用CRC16算法得到一个hash值,然后对16384取余,得到的结果就是slot值。

Redis-cli –cluster add-node 192.168.150.101:7004 192.168.150.101:7001 最后一个为集群中存在的节点ip。reshard重新分配插槽。

利用cluster failover命令可以手动让集群中的某个master宕机,切换到执行cluster failover命令的这个slave节点,实现无感知的数据迁移。
手动的Failover支持三种不同模式:

  • 缺省:默认的流程,如图1~6步
  • force:省略了对offset的一致性校验
  • takeover:直接执行第5步,忽略数据一致性、忽略master状态和其它master的意见
nephen wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!
坚持原创技术分享,您的支持将鼓励我继续创作!