Redis作为一个开源的、优秀的、基于内存的key-value
缓存及持久化存储解决方案,同时也提供了复制功能,本文主要介绍Redis的持久化功能和复制原理。
redis数据的操作在内存中完成,一旦服务器进程退出,数据就会丢失。为了解决这个问题,可将内存中的数据保存到磁盘中,避免数据的丢失,在重启服务时加载数据,实现持久化。Redis提供了两种持久化的方案:
RDB
持久化 以指定的时间间隔执行数据集的即时点(point-in-time)快照。AOF
持久化 在服务端记录每次收到的写操作,在服务器启动时会重放,以重建原始数据集。RDB持久化通过类似周期性快照的方式snapshot
,写操作存储为二进制文件。
客户端显式使用save
或bgsave
命令启动快照保存机制:
SAVE
(同步) 阻塞redis的服务器进程,直到RDB文件被创建完毕。BGSAVE
(异步) 派生(fork)一个子进程来创建新的RDB文件,记录接收到BGSAVE当时的数据库状态,进 程继续处理接收到的命令,子进程完成文件的创建之后,会发送信号给父进程,而与此同时,父进程处理 命令的同时,通过轮询来接收子进程的信号。BGSAVE
可以在不阻塞主进程的情况下完成数据的备份。可以通过redis.conf
中设置多个自动保存条件,只要有一个条件被满足,服务器就会执行BGSAVE
命令。
# redis默认配置策略,需要关闭时配置:SAVE ""
SAVE 900 1 # 服务器在900秒之内被修改了1次
SAVE 300 10 # 服务器在300秒之内被修改了10次
SAVE 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
的模式,每次都是全量的备份。
每次Redis收到修改数据集的命令,将会被追加到 AOF 中。当你重启 Redis 的时候,就会重放(re-play)AOF文件来重建数据库状态。
可以想象,写操作不断执行的时候 AOF文件会越来越大。例如,如果你增加一个计数器 100 次,你的数据集里只会有一个键存储这最终值,但是却有 100 条记录在 AOF 中。其中 99 条记录在重建当前状态时是不需要的。
于是 Redis 支持一个有趣的机制:在后台重建AOF而不影响服务客户端。每当你发送BGREWRITEAOF时,Redis将会写入一个新的AOF文件,包含重建当前内存中数据集所需的最短命令序列。如果你使用的是 Redis 2.2的AOF,你需要不时的运行 BGREWRITEAOF 命令。
日志重写采用了和快照一样的写时复制机制。过程如下:
#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
Redis 2.4 及以后的版本中,不允许在 RDB 快照操作运行过程中触发 AOF 重写,也不允许在 AOF 重写运行过程中运行 BGSAVE。这防止了两个 Redis 后台进程同时对磁盘进行繁重的 IO 操作。当在快照运行的过程中,用户使用 BGREWRITEAOF 显式请求日志重写操作的话,服务器会答复一个 OK 状态码,告诉用户这个操作已经被安排调度,等到快照完成时开始重写。
Redis 在同时开启 AOF 和 RDB 的情况下重启,会使用 AOF 文件来重建原始数据集,因为通常 AOF 文件是保存数据最完整的。
复制技术使得数据库的读写操作可以分散在运行于不同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,集群图如下:
上图为Redis复制工作过程:
同步是通过命令流完成的,和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 开始,在复制链接断开后,在从服务器再次连接主服务器时可以继续复制过程,而不需要一次完整的重同步。
master除了备份RDB文件之外还会维护者一个环形队列,以及环形队列的写索引和slave同步的全局offset,环形队列用于存储最新的操作数据,当slave和maste断开重连之后,会把slave维护的offset,也就是上一次同步到哪里的这个值告诉master,同时会告诉master上次和当前slave连接的master的runid,满足下面两个条件,Redis不会全量复制:
满足上面两个条件,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复制中的语义是这样的:
这个特性有两个配置参数:
min-slaves-to-write <number of slaves> # N的数量
min-slaves-max-lag <number of seconds> # 指定时间间隔
如果有至少N
个小于M
秒滞后的从服务器,写请求才会被接受。
你可能会认为这个像 CAP
理论中较宽松版本的C
,不能保证指定写的一致性,但是至少数据丢失的时间窗口被限制在一个指定的秒数内。
如果条件不满足,主服务器会返回一个错误,并且不会接受写请求。
马哥运维笔记
浅析 Redis 复制
redis官方文档