数据持久化

RDB

  • Redis Database Backup file(Redis数据备份文件),也叫做Redis数据快照。把内存中的所有数据都记录到磁盘中,故障重启后,从磁盘读取快照文件,恢复数据。
  • RDB文件称为快照文件,默认保存当前运行目录
1
2
3
4
redis-cli
save # 使用save命令来进行快照保存,由Redis主进程执行RDB,阻塞所有命令
# 所以一般不使用save,Redis在停机退出的时候会自动执行save,再次启动会读取RDB,实现默认的持久化
bgsave # 开启子进程执行RDB,避免主进程受到影响
  • redis.conf配置RDB机制
1
2
3
4
5
6
7
8
save 900 1 # 表示如果900s内至少有1个key被修改,则执行bgsave
save 300 10
save 60 10000
# 如果是save "" 表示禁用RDB

rdbcompression yes # 是否压缩,建议不开启,因为压缩也会消耗CPU
dbfilename dump.rdb # RDB文件名称
dir ./ # 文件保存路径目录
  • RDBbgsave开始时会fork主进程得到子进程,子进程共享主进程的内存数据。完成fork后读取内存数据并写入RDB文件
  • 但是fork的过程是阻塞的,主进程此时不能处理别的请求。所以要加快fork过程
  • 主进程操作虚拟内存,虚拟内存根据页表的映射关系到物理内存中真正的数据
  • fork仅仅是把页表做拷贝,即对映射关系做拷贝,因此子进程可以读取相同的数据,写入行的RDB文件
  • 为了避免在子进程读的时候主进程写,fork采用的是copy-on-write技术
    • 主要是将物理内存标记成read-only
    • 主进程读,则访问共享内存
    • 主进程写,则拷贝一份数据,对副本执行写操作,写完后页表映射关系修改到副本
    • 极端情况下就是所有数据都要拷贝一份,内存翻倍。所以一般情况下Redis需要预留内存
      copyonwrite
  • RDB方式bgsave的基本流程

    • fork主进程得到一个子进程,共享内存空间
    • 子进程读取内存数据并写入新的RDB文件
    • 用新的RDB文件替换旧的RDB文件
  • RDB缺点

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

AOF

  • Apppend Only File(追加文件),每一个写命令都会记录在AOF中,可以看作是命令日志文件
  • 默认关闭
1
2
3
4
5
6
appendonly yes # 是否开启AOF功能,默认为no
appendfilename "appendonly.aof" # AOF文件名

appendfsync always # 每次执行一次写命令,立即记录到AOF文件中
appendfsync everysec # 写命令执行完写放入AOF缓存,每隔1s将缓冲区数据写到AOF文件,默认方案
appendfsync no # 写命令执行完先放入AOF缓存,操作系统决定何时将缓冲区内容写回磁盘
配置项 刷盘时机 优点 缺点
always 同步刷盘 可靠性高,几乎不丢数据 性能影响大
everysec 每秒刷盘 性能适中 最多丢失1s数据
no 操作系统控制 性能最好 可靠性较差,可能丢失大量数据
  • 因为同时记录了命令和数据,所以AOF会比RDB文件大很多,并且会记录同一个key的多次写操作,但是只有最后一次的写才有意义,因此可以执行bgrewriteaof命令,让AOF执行重写,用最少的命令达到原来操作相同的结果
  • 可以在redis.conf中配置AOF重写机制
1
2
auto-aof-rewrite-percentage 100 # AOF文件比上次文件增占了多少百分比则触发重写
auto-aof-rewrite-min-size 64mb # AOF文件体积最小多大以上才触发重写

RDB VS AOF

RDB AOF
持久化方式 定时对整个内存快照 记录每次执行命令
数据完整性 不完整,两次备份之间会丢失 相对完整,取决于刷盘策略
文件大小 会有压缩,文件体积小 记录命令,文件体积大
宕机恢复速度 很快
数据恢复优先级 低,因为数据完整性不如AOF 高,因为数据完整性更高
系统资源占用 高,大量CPU和内存消耗 低,主要是磁盘IO资源,但是AOF重写会占用大量的CPU和内存资源
使用场景 可以容忍数分钟的数据丢失,更快启动速度 对数据安全性要求较高时常见

Redis主从集群

  • 单节点Redis并发能力有上限,进一步提高Redis的并发能力,就需要搭建主从集群,实现读写分离
  • 启动三个Redis服务,需要配置三台主从关系
    • 临时配置: replicaof <masterip> <masterport>
    • 永久配置: 在redis.conf中添加一行: replicaof <masterip> <masterport>

数据同步原理

  • 主从第一次同步是全量同步,slave重启后同步,则执行增量同步
  • 如何判断是否第一次同步:
    • replication ID: 简称replid是数据集的标记,ID一致则说明是同一个数据集,每个master都有唯一的replid,slave则会继承master节点的replid
    • offset偏移量,随着记录在repl_baklog中的数据增多而逐渐增大,slave完成同步时也会记录当前同步的offset,如果slaveoffset小于masteroffset,则说明slave数据落后于master,需要更新
    • 基于replid判断主从是否一致,如果不一致则表明是第一次同步

first

全量同步的流程:

  1. slave节点请求增量同步
  2. master节点判断replid,不一致,则拒绝增量同步
  3. master将完整内存数据生成RDB,发送到slave
  4. slave清空本地数据,加载masterRDB
  5. masterRDB期间的命令记录在repl_backlog,持续将log中的命令发送给slave
  6. slave执行接收到的命令,保持与master之间的同步

second

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

优化主从集群:

  1. master中配置repl-diskless-sync yes启用无磁盘复制,避免全量同步时的磁盘IO(一般是用于磁盘比较慢,但是网络较快的场景)
  2. Redis单节点上的内存占用不要太大,减少RDB导致的过多的磁盘IO
  3. 减少全量同步的次数: 提高repl_baklog的大小,发现slave宕机则尽快实现故障恢复,避免全量同步
  4. 限制一个master上的slave数量,如果很多slave则使用主从从的链式结构,减少master压力

增量同步 VS 全量同步

  1. 全量同步: master将完整内存数据生成RDB,发送到slave,后续命令记录在repl_baklog中,逐个发送给slave
  2. 增量同步: slave提交自己的offsetmaster,获取repl_baklog中从offset之后的命令给slave

哨兵Sentinel

作用

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

判断Redis是否健康

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

  • 主观下线sdown: 某个sentinel发现节点实例没有在规定时间内响应,则认为其主观下线
  • 客观下线odown: 超过指定数量quorumsentinel都认为该实例主观下线,则其客观下线,quorum最好超过sentinel的一半

主从切换选择依据

  1. 判断slave节点与master节点断开时间长短,超过指定值down-after-milliseconds * 10会排除该slave节点
  2. 判断slave节点的slave-prority越小优先级越高,如果为0则永不选举
  3. 如果slave-prority则判断slaveoffset值,越大越新,优先级越高
  4. 最后判断slave节点的运行ID大小,越小优先级越高

故障转移

  1. sentinelslave1发送slaveof no one ,让节点称为master
  2. sentinel给其他slave发送slaveof ip port命令,让这些slave称为新的master从节点,开始从新的master上同步数据
  3. sentinel将故障节点标记为slave,故障节点恢复后悔自动成为新的masterslave

Redis分片集群

主从集群的问题:

  1. 海量数据存储
  2. 高并发写

分片集群特征

  1. 集群中有多个master,每个master保存不同数据(并发写)
  2. 每个master可以有多个slave(并发读)
  3. master通过PING监测彼此健康状态
  4. 客户端请求可以访问集群的任意节点,最终都会被转发到正确的节点
    cluster

Redis会把每一个master映射到0~16383共16384个hash slot上,查看集群信息就可以看到
数据key不是与节点绑定,而是与插槽绑定。redis会根据key的有效部分计算插槽值,分两种情况

  • key中包含"{}“,且”{}“中至少包含一个字符,”{}"中的部分是有效部分
  • key中国不包含"{}",整个key都是有效部分
    利用CRC16算法得到一个哈希值,然后对16384取余,得到的结果就是slot
    如何将同一类数据固定的保存在同一个Redis实例中:
  • 这一类数据使用相同的有效部分,比如key都以{typeID}为前缀

故障转移

集群中一个master宕机会发生什么?

  1. 实例与其他实例失去链接
  2. 疑似宕机
  3. 确定下线,自动提升一个slave成为新的master

数据迁移

  • 利用cluster failover命令可以手动让集群中的某个master宕机,切换到执行cluster failover命令的这个slave及诶单,实现无感知的数据迁移,具体流程为:
    movedata
  • 手动的failover支持三种不同的模式:
    • 默认的流程,例如1~6
    • force省略了对offset的一致性校验
    • takeover直接执行第五步,忽略数据一致性,忽略master状态和其他master意见