Redis持久化和复制

Redis作为一个开源的、优秀的、基于内存的key-value缓存及持久化存储解决方案,同时也提供了复制功能,本文主要介绍Redis的持久化功能和复制原理。

redis持久化

redis数据的操作在内存中完成,一旦服务器进程退出,数据就会丢失。为了解决这个问题,可将内存中的数据保存到磁盘中,避免数据的丢失,在重启服务时加载数据,实现持久化。Redis提供了两种持久化的方案:

  • RDB持久化 以指定的时间间隔执行数据集的即时点(point-in-time)快照。
  • AOF持久化 在服务端记录每次收到的写操作,在服务器启动时会重放,以重建原始数据集。

RDB持久化

RDB持久化通过类似周期性快照的方式snapshot,写操作存储为二进制文件。

RDB的创建与载入

客户端显式使用savebgsave命令启动快照保存机制:

  • SAVE(同步) 阻塞redis的服务器进程,直到RDB文件被创建完毕。
  • BGSAVE(异步) 派生(fork)一个子进程来创建新的RDB文件,记录接收到BGSAVE当时的数据库状态,进 程继续处理接收到的命令,子进程完成文件的创建之后,会发送信号给父进程,而与此同时,父进程处理 命令的同时,通过轮询来接收子进程的信号。
自动保存间隔

BGSAVE可以在不阻塞主进程的情况下完成数据的备份。可以通过redis.conf中设置多个自动保存条件,只要有一个条件被满足,服务器就会执行BGSAVE命令。

# redis默认配置策略,需要关闭时配置:SAVE ""
SAVE 900 1      # 服务器在900秒之内被修改了1SAVE 300 10     # 服务器在300秒之内被修改了10SAVE 60 10000   # 服务器在60秒之内被修改了10000# 其他选项
stop-writes-on-bgsave-error yes  # 在数据同步有错时是否通知操作
rdbcompression yes               # 数据是否压缩
rdbchecksum yes                  # 是否校验
dbfilename dump.rdb              # 文件名称
dir /var/lib/redis               # 文件存放路径

RDB文件的载入一般情况是自动的,redis服务器启动的时候,redis服务器再启动的时候如果检测到RDB文件的存在,那么redis会自动载入这个文件。

如果服务器开启了AOF持久化,那么服务器会优先使用AOF文件来还原数据库状态。

RDB是通过保存键值对来记录数据库状态的,采用copy on write的模式,每次都是全量的备份。

AOF持久化

实现过程

每次Redis收到修改数据集的命令,将会被追加到 AOF 中。当你重启 Redis 的时候,就会重放(re-play)AOF文件来重建数据库状态。

可以想象,写操作不断执行的时候 AOF文件会越来越大。例如,如果你增加一个计数器 100 次,你的数据集里只会有一个键存储这最终值,但是却有 100 条记录在 AOF 中。其中 99 条记录在重建当前状态时是不需要的。
于是 Redis 支持一个有趣的机制:在后台重建AOF而不影响服务客户端。每当你发送BGREWRITEAOF时,Redis将会写入一个新的AOF文件,包含重建当前内存中数据集所需的最短命令序列。如果你使用的是 Redis 2.2的AOF,你需要不时的运行 BGREWRITEAOF 命令。

日志重写采用了和快照一样的写时复制机制。过程如下:

  • Redis调用fork()创建子进程。
  • 子进程开始向一个临时文件中写 AOF。
  • 父进程在一个内存缓冲区中积累新的变更(同时将新的变更写入旧的 AOF 文件,所以即使重写失败我们也安全)。
  • 当子进程完成重写文件,父进程收到一个信号,追加内存缓冲区到子进程创建的文件末尾。
  • Redis 自动重命名旧文件为新的AOF文件,然后开始追加新数据到新文件。
相关选项
#AOF 和 RDB 持久化方式可以同时启动并且无冲突。  
#如果AOF开启,启动redis时会优先加载AOF文件,这些文件能够提供更好的保证。 
appendonly yes

# 只增文件的文件名称。(默认是appendonly.aof)  
# appendfilename appendonly.aof 

#redis支持三种不同的写入方式:  
#  
# no:不调用,之等待操作系统来清空缓冲区当操作系统要输出数据时。很快。  
# always: 每次更新数据都写入仅增日志文件。慢,但是最安全。
# everysec: 每秒调用一次,折中方案。
appendfsync everysec  

# 设置为yes表示rewrite期间对新写操作不fsync,暂时存在内存中,等rewrite完成后再写入.官方文档建议如果你有特殊的情况可以配置为'yes'。但是配置为'no'是最为安全的选择。
no-appendfsync-on-rewrite no  

# 自动重写只增文件。  
# redis可以自动盲从的调用‘BGREWRITEAOF’来重写日志文件,如果日志文件增长了指定的百分比。  
# 当前AOF文件大小是上次日志重写得到AOF文件大小的二倍时,自动启动新的日志重写过程。
auto-aof-rewrite-percentage 100  
# 当前AOF文件启动新的日志重写过程的最小值,避免刚刚启动Reids时由于文件尺寸较小导致频繁的重写。
auto-aof-rewrite-min-size 64mb

AOF和RDB 的相互作用

Redis 2.4 及以后的版本中,不允许在 RDB 快照操作运行过程中触发 AOF 重写,也不允许在 AOF 重写运行过程中运行 BGSAVE。这防止了两个 Redis 后台进程同时对磁盘进行繁重的 IO 操作。当在快照运行的过程中,用户使用 BGREWRITEAOF 显式请求日志重写操作的话,服务器会答复一个 OK 状态码,告诉用户这个操作已经被安排调度,等到快照完成时开始重写。

Redis 在同时开启 AOF 和 RDB 的情况下重启,会使用 AOF 文件来重建原始数据集,因为通常 AOF 文件是保存数据最完整的。


redis复制

复制技术使得数据库的读写操作可以分散在运行于不同CPU之上的独立服务器上,同时也实现数据冗余。
Redis 的复制 (replication) 是一种使用和配置起来非常简单的主从(master-slave)复制,允许 Redis 从服务器成为主服务器的精确副本。

复制的实现

redis支持一台物理机启动多个服务以实现,本次实例将在一台物理机上实现:
启动master实例:

# master节点
/usr/bin/redis-server --port 8000 &

# slave 节点
/usr/bin/redis-server --port 8001 --slaveof 127.0.0.1 8000 &
/usr/bin/redis-server --port 8002 --slaveof 127.0.0.1 8000 &
/usr/bin/redis-server --port 8003 --slaveof 127.0.0.1 8000 &

# 查看端口
ss -tnl 

LISTEN      0      128       *:8000                  *:*                  
LISTEN      0      128       *:8001                  *:*                  
LISTEN      0      128       *:8002                  *:*                  
LISTEN      0      128       *:8003                  *:*  

这样,我们就成功的启动了四台Redis实例,master实例的服务端口为8000,R1、R2、R3的服务端口分别为8001、8002、8003,集群图如下:

enter description here

复制过程

enter description here

上图为Redis复制工作过程:

  • slave向master发送sync命令。
  • master开启子进程来讲dataset写入rdb文件,同时将子进程完成之前接收到的写命令缓存起来。
  • 子进程写完,父进程得知,开始将RDB文件发送给slave。
  • master发送完RDB文件,将缓存的命令也发给slave。
  • master增量的把写命令发给slave。

同步是通过命令流完成的,和Redis的协议一样的格式。
用telnet 连接上Redis 的端口,然后发送 SYNC 命令,master会回应消息:

[root@node3 ~]# telnet  127.0.0.1 8000 
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
SYNC            # 发送SYNC命令
$77
REDIS0007   redis-ver3.2.10
redis-bits????e?Lused-mem?X?\eRXshell*1
$4
PING

部分重同步

当主从链路由于某些原因断开时,从服务器可以自动重连。如果主服务器收到多个并发的从服务器的同步请求,只会执行一个后台保存来服务所有从服务器。
从 Redis 2.8 开始,在复制链接断开后,在从服务器再次连接主服务器时可以继续复制过程,而不需要一次完整的重同步。

enter description here

master除了备份RDB文件之外还会维护者一个环形队列,以及环形队列的写索引和slave同步的全局offset,环形队列用于存储最新的操作数据,当slave和maste断开重连之后,会把slave维护的offset,也就是上一次同步到哪里的这个值告诉master,同时会告诉master上次和当前slave连接的master的runid,满足下面两个条件,Redis不会全量复制:

  • slave传递的run id和master的run id一致。
  • master在环形队列上可以找到对应的offset值。

满足上面两个条件,Redis就不会全量复制,这样的好处是大大的提高的性能,不做无效的功

复制是由psync命令实现的,slave可以通过psync命令来让Redis进行增量复制,当然最终是否能够增量复制取决于环形队列的大小和slave的断线时间长短和重连的这个master是否是之前的master。

配置相关参数


repl-backlog-size 1mb      # 环形队列大小
repl-backlog-ttl 3600      # 没有slave需要同步的时候,多久可以释放环形队列
repo-diskless-sync no      # 否开启Diskless Replication(免持久化)的开关配置
repl-diskless-sync-delay 5 # 指定复制开始的时间延迟

从服务器只读

从 Redis 2.6 开始,从服务器支持默认开启的只读模式。这个行为由 redis.conf 文件中的 slave-read-only 选项控制,可以在运行时使用 CONFIG SET 来开启和关闭。
这种特性不能意味着暴露从服务器到互联网,因为诸如 DEBUG 和 CONFIG 这样的管理命令等仍可用。但是可以通过在 redis.conf 中使用 rename-command 指令来禁止命令。
这些写入的数据会在从服务器和主服务器重同步时,或者从服务器重启时被丢弃,还是有一些存储一些短暂的数据到可写的从服务器的合理场景。
比如:主服务器故障,需要其他节点作为主服务器时。

半同步复制

从 Redis 2.8 开始,可以设置 Redis 主服务器在当前至少拥有 N 个从服务器的连接的情况下,才能接受写请求。
和MySQL复制策略有点类似,Redis复制本身是异步的,但也提供了半同步的复制策略,半同步复制策略在Redis复制中的语义是这样的:

  • Redis 从服务器每秒种 ping 主服务器,上报处理完的复制流的数据量。
  • Redis 主服务器记录上一次从每一个从服务收到 ping 的时间。
  • 用户可以配置最小从服务器数量,每台从服务器拥有一个不大于最大秒数的滞后(lag)。

这个特性有两个配置参数:

min-slaves-to-write <number of slaves>   # N的数量
min-slaves-max-lag <number of seconds>   # 指定时间间隔

如果有至少N个小于M秒滞后的从服务器,写请求才会被接受。
你可能会认为这个像 CAP 理论中较宽松版本的C,不能保证指定写的一致性,但是至少数据丢失的时间窗口被限制在一个指定的秒数内。
如果条件不满足,主服务器会返回一个错误,并且不会接受写请求。

参考

马哥运维笔记
浅析 Redis 复制
redis官方文档

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值