Redis支持RDB和AOF两种数据持久化机制,持久化功能可以有效的避免因进程退出造成的数据丢失问题,当下次重启时利用之前持久化的文件即可实现数据恢复。
RDB持久化是把当前进程数据生成快照保存到硬盘的过程,触发RDB持久化过程分为手动触发和自动触发。
手动触发分别对应save和bgsave:
示例:
[root@redis opt]# mkdir redis/data
[root@redis opt]# vim redis/redis.conf
#保证以下两项是正常的状态
dbfilename dump.rdb # 定义数据持久化的文件名
dir /opt/redis/data # 定义数据持久化的文件存放目录
#如果进行修改修改,需重启redis服务
[root@redis opt]# redis-cli
127.0.0.1:6379> save
OK
127.0.0.1:6379> bgsave
Background saving started
[root@redis opt]# ll redis/data/
总用量 4
-rw-r--r-- 1 root root 92 4月 5 17:58 dump.rdb
在每次redis启动时,都会去指定目录下寻找dump.rdb文件并读取它,把其中的数据读取到redis中,这是它可以做到数据持久化的根本原因。
四种自动触发的情况:
[root@redis opt]# vim redis/redis.conf
#以下三行是数据持久化的策略,若要停止数据持久化,只需将其都注释掉即可
save 900 1 # 每900秒(15分钟)如果有1个key发生变化,则保存
save 300 10 # 每300秒(5分钟)如果有10个key发生变化,则保存
save 60 10000 # 每60秒(1分钟)如果有10000个key发生变化,则保存
dbfilename dump.rdb # 保存的文件名
dir /usr/local/redis/data # 文件的存放路径
stop-writes-on-bgsave-error yes # 该值为yes的话,在redis持久化数据到磁盘时如果出现失败,redis会停止接受所有的写请求。
rdbcompression yes #对存储到磁盘中的快照数据,进行压缩存储
rdbchecksum yes #表示在存储快照后,我们还可以让redis使用CRC64算法来进行数据校验,但是这样会增加大约10%的性能消耗,如果希望可以获取到最大的性能提升,可以关闭该功能
默认redis使用的是rdb方式持久化,这种方式在许多应用中已经足够用了。但是redis如果中途宕机,会导致可能有几分钟的数据丢失,根据save来策略进行持久化,Append Only File是另一种持久化方式,可以提供更好的持久化特性。Redis会把每次写入的数据在接收后都写入appendonly.aof文件,每次启动时Redis都会先把这个文件的数据读入内存里,先忽略RDB文件。
[root@redis opt]# vim redis/redis.conf
appendonly no # 默认关闭aof持久化方式,将其设置为yes可以开启aof持久化
appendfilename "appendonly.aof" # aof文件名
appendfsync always
appendfsync everysec
appendfsync no
上面三行是aof方式的持久化策略:
auto-aof-rewrite-percentage 100
aof自动重写配置,当目前aof文件大小超过上一次重写的aof文件大小的百分之多少进行重写,即当aof文件增长到一定大小的时候,Redis能够调用bgrewriteaof对日志文件进行重写。当前AOF文件大小是上次日志重写得到AOF文件大小的二倍(设置为100)时,自动启动新的日志重写过程。
auto-aof-rewrite-min-size 64mb
设置允许重写的最小aof文件大小,避免了达到约定百分比但尺寸仍然很小的情况还要重写,默认这个值为64M,生产中根据实际情况,可能会指定为几个G。
在分布式系统中,为了解决单点问题,通常会把数据复制多个副本部署到其他节点,以便满足故障恢复和负载均衡等需求。redis也是如此,它为我们提供了复制功能,实现了相同数据的多个副本。复制功能是redis高可用的基础,不管是哪种集群方案,都是基于底层的主从复制原理进行的。
在redis的主从复制中,和其他服务一样,都有master和slave两个角色,默认每个redis节点都是主节点,每个从节点也只能有一个主节点,而主节点可以配置多个从节点。
配置主从复制有以下三种方案:
上述几种方案比较简单,这里以第二种方案进行举例即可(我这里环境为一个节点使用不同端口配置了多个实例):
#启动两个redis实例,仅仅监听的端口不一样
[root@redis redis]# redis-server /opt/redis/redis_6379.conf
[root@redis redis]# redis-server /opt/redis/redis_6380.conf --slaveof 192.168.1.5 6379
#注意slave实例的启动方式
[root@redis opt]# redis-server /opt/redis/redis_6381.conf
#创建几个副本实例
[root@redis opt]# redis-cli -h 192.168.1.5 -p 6379 #6379端口为master
192.168.1.5:6379> keys *
1) "a"
#现在启动一个6380端口的redis,指定端口6379的redis为master
[root@redis redis]# redis-cli -h 192.168.1.5 -p 6380 # # 6380端口为slave
192.168.1.5:6380> keys * #确实已经同步6379实例中的数据
1) "a"
[root@redis redis]# redis-cli -h 192.168.1.5 -p 6379
192.168.1.5:6379> set test redis
#master实例添加数据
[root@redis redis]# redis-cli -h 192.168.1.5 -p 6380
192.168.1.5:6380> keys *
1) "a"
2) "test"
#从节点获取数据(确实已经同步主节点中的数据)
192.168.1.5:6380> info replication #查看slave节点的详细信息
Replication
role:slave #角色为slave
master_host:192.168.1.5 #master节点的IP
master_port:6379 #master节点的监听地址
master_link_status:up #和master的连接状态,up表示正常,down则表示不正常
master_last_io_seconds_ago:1 #最后一次和master节点进行交互是在1秒前
master_sync_in_progress:0
slave_repl_offset:308 #复制偏移量,这个值一直在变,master和slave的这个值可能会不一样,但不会相差太多
slave_priority:100
slave_read_only:1 #slave是否为只读,1表示只读,0表示可读写,可将配置文件中“replica-read-only yes”的值修改为no,表示可读写,但不建议这么做
connected_slaves:0 #本实例连接的slave数量为0
master_replid:1a358bd61e0fc5fa4ad28da5ae4393a0b75aaa08
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:308
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:308
192.168.1.5:6380> slaveof no one
#只需在slave节点执行此命令,即可断开主从复制
当断开主从复制关系,slave节点上发生了什么?
所谓切主操作,无非就是直接重新指向另一台redis节点作为新的master。如下:
[root@redis opt]# redis-cli -h 192.168.1.5 -p 6380 #登录6380实例进行查看
192.168.1.5:6380> slaveof no one
OK
192.168.1.5:6380> slaveof 192.168.1.5 6381 #切换6381实例为master
OK
192.168.1.5:6380> info replication #查看6380实例的详细信息
# Replication
role:slave
master_host:192.168.1.5
master_port:6381 #当前master为6381端口的redis实例
master_link_status:up
master_last_io_seconds_ago:7
............... # 省略部分内容
192.168.1.5:6380> keys * #可以看出已经同步了6381实例中的数据
1) "lc"
2) "l"
3) "n"
192.168.1.5:6380> slaveof no one
OK
192.168.1.5:6380> slaveof 192.168.1.5 6379 #再次切换6379位master
OK
192.168.1.5:6380> keys * #可以看出已经同步了6379实例中的数据,并且将同步6381实例中的数据进行了删除
1) "a"
2) "test"
当执行切主指令后,slave发生了什么?
对于数据比较重要的节点,主节点会通过设置requirepass参数进行设置密码,这时,所有的client访问都需要使用auth进行认证,因此,在配置主从时,需要配置slave节点的masterauth参数与master的requirepass参数一致,这样从节点才可以正确的连接到主节点并发起复制流程。
默认情况下,slave使用slave-read-only=yes配置为只读模式。由于复制只能从master到slave,对于slave的任何修改master都无法感知,修改slave节点数据会造成主从数据不一致。因此建议线上不要修改从节点的只读模式。
主从节点一般部署在不同机器上,主从复制时的网络延迟就成为需要考虑的问题,Redis为我们提供了repl-disable-tcpnodelay参数用于控制是否关闭TCP_NODELAY,默认关闭,说明如下:
Redis的复制拓扑结构可以支持单层或多层复制关系,根据拓扑复杂性可以分为以下三种:一主一从、一主多从、树状主从结构,下面来聊聊不同拓扑的区别。
主从复制流程图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0wP4V3RV-1616914536771)(C:/Users/%E7%8E%8B%E5%85%88%E7%94%9F/Desktop/%E7%85%A7%E7%89%87/%E6%95%B0%E6%8D%AE%E5%BA%93/7.png)]
流程:
Redis在2.8及以上版本使用psync命令完成主从数据同步。现在应该不会有公司用2.8以下的版本了吧?
同步过程分为:
参与复制的master节点都会维护自身复制偏移量,主节点在处理完写入命令后,会把命令的字节长度做累计记录,统计在info replication命令下的参数master_replication,slave节点每秒上报自身复制的偏移量给主节点,主节点也会保存从节点的复制偏移量。如下:
192.168.1.5:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.1.5,port=6380,state=online,offset=8778,lag=0
master_replid:7236a4891f3026f9133b28f9f54755c3cac2b8db
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:8778
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:8778
复制积压缓冲区是保存在主节点上的一个固定长度的队列,默认大小为1MB,复制积压缓冲区的作用为:当master向slave节点同步数据的过程中,此时,master还有可能有新数据继续写入,这些新写入的数据master并不会立即发送给slave节点,而是先写入复制积压缓冲区,待下次同步数据时,遵循先进先出的原则再同步到slave。
复制缓冲区相关配置如下:
repl-backlog-size 1mb # 指定积压缓冲区的大小
info replication指令查看到的相关信息如下:
192.168.1.5:6380> info replication
repl_backlog_active:1 #开启复制缓冲区
repl_backlog_size:1048576 #缓冲区最大长度
repl_backlog_first_byte_offset:2626 #起始偏移量,计算当前缓冲区可用范围
repl_backlog_histlen:5138 #已保存数据的有效长度
master节点的运行ID
每个redis节点启动后都会分配一个动态的运行ID,每重启一次此ID都会发生变化,运行ID的作用是来表示唯一识别redis的节点,用命令info server可以查看当前节点的运行ID,如下:
192.168.1.5:6380> info server
# Server
redis_version:5.0.8
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:fa5647e7230f17e4
redis_mode:standalone
os:Linux 3.10.0-862.el7.x86_64 x86_64
arch_bits:64
multiplexing_api:epoll
atomicvar_api:atomic-builtin
gcc_version:4.8.5
process_id:60925
run_id:735303f005fe9834c8cc1b76ccc7e09f204bd13a #运行的ID
tcp_port:6380
uptime_in_seconds:7935
uptime_in_days:0
hz:10
configured_hz:10
lru_clock:9114159
executable:/opt/redis-server
config_file:/opt/redis/redis_6380.conf
流程:
部分复制主要是Redis针对全量复制的过高开销做出的一种优化措施,使用psync (runid) (offset)命令实现。当slave正在复制master时,如果出现网络闪断或者命令丢失等异常情况时,slave会向master要求补发丢失的数据,如果master的复制积压缓冲区内存在这部分数据则直接发送给从节点,这样就可以保持主从节点复制的一致性。补发的这部分数据一般远远小于全量数据,所以开销很小。
流程:
主从节点在建立复制后,它们之间维护着长连接并彼此发送心跳命令。
主从心跳判断机制:
Replconf作用:
192.168.1.5:6379> client list
id=7 addr=192.168.1.5:36707 fd=7 name= age=7525 idle=1 flags=S db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 events=r cmd=replconf
id=9 addr=192.168.1.5:54246 fd=8 name= age=6 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=26 qbuf-free=32742 obl=0 oll=0 omem=0 events=r cmd=client
主节点不但负责数据读写,还负责把写命令同步给从节点。写命令的发送过程是异步完成,也就是说主节点自身处理完写命令后直接返回给客户端,并不等待从节点复制完成。
主节点复制流程:
由于主从复制过程是异步的,就会造成从节点的数据相对主节点存在延迟。具体延迟多少字节,我们可以在主节点执行info replication命令查看相关指标获得。如下:
192.168.1.5:6379> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.1.5,port=6380,state=online,offset=11424,lag=0
master_replid:7236a4891f3026f9133b28f9f54755c3cac2b8db
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:11424
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:11424
在上面可以看到slave0的信息,分别记录了从节点的ip和port,以及是否online(在线),offset表示当前从节点的复制偏移量,master_repl_offset
表示当前主节点的复制偏移量,两者之间的差值就是当前从节点复制延迟量。redis的复制速度取决于主从之间的网络环境。