缓存系列之四:redis持久化与redis主从复制

一:redis 虽然是一个内存级别的缓存程序,即redis 是使用内存进行数据的缓存的,但是其可以将内存的数据按照一定的策略保存到硬盘上,从而实现数据持久保存的目的,redis支持两种不同方式的数据持久化保存机制,分别是RDB和AOF,具体实现如下:

1.1:redis数据持久化概括:
redis 支持两种数据持久保存方式,分别是RDB和APF
RDB可以设置在多长时间内有多少个key发生变化就执行RDB快照。
AOF持久化是服务器记录和保存redis执行的所有命令,并在服务重启的时候重新执行全部的命令来还原数据,AOF文件的命令全部使用redis 协议格式保存,执行的命令会被以追加的命令保存到文件的末尾,另外redis还可以在后台对AOF文件进行rewrite即重写,这样可以使文件的大小不会超过保存数据所需要的实际大小。
redis可以同时使用AOF和RDB进行数据持久化,但是当redis重启的时候会优先使用AOF文件来还原数据,这是因为AOF保存的文件通常要比RDB文件保存的数据更加完整。

1.2:RDB的实现与优缺点:
1.2.1:实现原理:
RDB是redis从主进程先fork出一个子进程,使用写时复制机制,子进程将内存的数据保存为一个临时文件,比如dump.rdb.temp,当数据保存完成之后再将上一次保存的RDB文件替换掉,然后关闭子进程,这样可以保存每一次做RDB快照的时候保存的数据都是完整的,因为直接替换RDB文件的时候可能会出现突然断电等问题而导致RDB文件还没有保存完整就突然关机停止保存而导致数据丢失的情况,可以手动将每次生成的RDB文件进程备份,这样可以最大化保存历史数据。

1.2.2:RDB的优点:
保存了某个时间点的数据,可以通过脚本执行bgsave命令自定义时间点北备份,可以保留多个备份,当出现问题可以恢复到不同时间点的版本。
可以最大化io的性能,因为父进程在保存RDB 文件的时候唯一要做的是fork出一个子进程,然后的操作都会有这个子进程操作,父进程无需任何的IO操作
RDB在大量数据比如几个G的数据,恢复的速度比AOF的快

1.2.3:RDB的缺点:
不能时时的保存数据,会丢失自上一次执行RDB备份到当前的内存数据
数据量非常大的时候,从父进程fork的时候需要一点时间,可能是毫秒或者秒

1.3:AOF的实现和优缺点:
1.3.1:AOF的实现机制:
AOF和RDB一样使用了写时复制机制,AOF默认为每秒钟fsync一次,即将执行的命令保存到AOF文件当中,这样即使redis服务器发生故障的话顶多也就丢失1秒钟之内的数据,也可以设置不同的fsync策略,或者设置每次执行命令的时候执行fsync,fsync会在后台执行线程,所以主线程可以继续处理用户的正常请求而不受到写入AOF文件的IO影响
AOF文件是一个进行追加操作的日志文件(append only log),即操作的命令都会被追加到文件的末尾,如果因为磁盘满、突然停机等问题导致写入了不完整的命令,可以通过redis自带的命令redis-check-aof命令轻易的修复AOF文件,如下:
# redis-check-aof /data/redis/appendonly.aof
AOF analyzed: size=7683, ok_up_to=7683, diff=0
AOF is valid
redis可以在AOF文件变得过大的时候自带在后台对文件进行重写,重写后的新APF文件保存了恢复的时候需要的最新数据和最小的命令集合,这个操作是绝对安全的,因为redis 在创建新AOF文件的过程中会继续将命令追加到之前的AOF文件,这样即使服务器宕机也不会影响到当前redis 使用的AOF文件,而一旦新的AOF文件创建完成,redis就会从旧AOF文件切换到新的AOF文件,并将命令追加到新的AOF文件
AOF文件按照命令的执行顺序保存了对redis 的所以操作,使用的是redis协议,因此可以对AOF文件进行读取和分析,也可以导出,假如不小心执行了一个FLUSHALL命令,但是只要AOF文件还没有被重写可以立即停止服务器,然后删除AOF文件末尾的FLUSHALL命令并重启redis,即将可以将数恢复到FLUSHALL命令之前的数据状态。

1.3.2:AOF的缺点:
AOF的文件大小要大于RDB格式的文件
曾经有bug导致AOF重新载入AOF文件的时候无法将数据恢复到保存的时候远洋,比如执行阻塞命令BRPOPLPUSH就曾引起bug
根据所使用的fsync策略(fsync是同步内存中redis所有已经修改的文件到存储设备),默认是appendfsync everysec即每秒执行一次fsync

1.4:redis 持久化的配置参数:
1.4.1:编辑配置那文件:
# vim /etc/redis/6379.conf

save 900 1 #900秒以内有1个key发生变化就快照
save 300 10 #300秒以内有10个key发生变化就快照
save 60 10000 #60秒内有10000个key变化就快照
rdbcompression yes #持久化到RDB文件时,是否压缩,"yes"为压缩,"no"则反之
rdbchecksum yes #读取和写入的时候是否支持CRC64校验,默认是开启的
dbfilename dump.rdb #文件名
dir /data/redis #保存路径
slave-read-only yes #从redis 只读
maxmemory 512M #限制redis 最大使用内存
appendonly yes #是否开启AOF
appendfilename "appendonly.aof" #保存路径,和RDB在一起
appendfsync everysec #每秒执行一次fsync,即将内存更改的数据写入磁盘

[root@node1 src]# egrep '^[a-z]' /etc/redis/6379.conf

bind 0.0.0.0
protected-mode yes
port 6379
tcp-backlog 511
timeout 0
tcp-keepalive 300
daemonize yes
supervised no
pidfile /var/run/redis_6379.pid
loglevel notice
logfile ""
databases 16
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir /data/redis
slave-serve-stale-data yes
slave-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5
repl-disable-tcp-nodelay no
slave-priority 100
maxmemory 512M
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
lua-time-limit 5000
slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
notify-keyspace-events ""
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-size -2
list-compress-depth 0
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
hll-sparse-max-bytes 3000
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
aof-rewrite-incremental-fsync yes

SAVE 与 BGSAVE:
SAVE:阻塞保存
BGSAVE:在后台保存,不阻塞
KILL -9 会丢失自上次保存以后到现在的数据

1.4.2:重启redis服务:

[root@node1 ~]# /etc/init.d/redis stop
Stopping ...
Redis stopped
[root@node1 ~]# /etc/init.d/redis start
Starting Redis server...

1.4.2:通过python连接到redis 写入几个key,验证持久保存:

#!/bin/env  python
import redis

pool = redis.ConnectionPool(host='192.168.3.199',port=6379)
r = redis.Redis(connection_pool = pool)
for i in range(1000):
    r.set('k%d' % i,'v%d' % i)
    data = r.get('k%d' % i)
    print(data)

1.4.3:redis服务器验证数据:

[root@node1 ~]# redis-cli -h 192.168.3.199
192.168.3.199:6379> keys *
1) "k941"
2) "k863"
3) "k699"
4) "k646"
5) "k833"
6) "k945"
7) "k286"
8) "k310"
9) "k546"
10) "k313"
11) "k135"
12) "k458"
......

1.4.4:验证AOF和RDB持久化保存的文件,保存的文件路径是在redis.conf文件定义的:
[root@node1 ~]# ll /data/redis/
total 48
-rw-r--r-- 1 root root 32803 Jul 11 01:51 appendonly.aof
-rw-r--r-- 1 root root 10862 Jul 11 02:03 dump.rdb
AOF是时时写入到文件的,rdb需要等达到指定的策略后才会备份

二:redis主从:
redis支持主从复制的功能,可以让从服务器从主服务器备份数据,而且从服务器还可与有从服务器,即另外一台redis服务器可以从一台从服务器进行数据同步,redis 的主从同步是非阻塞的,其收到从服务器的sync(2.8版本之前是PSYNC)命令会fork一个子进程在后台执行bgsave命令,并将新写入的数据写入到一个缓冲区里面,bgsave执行完成之后并生成的将RDB文件发送给客户端,客户端将收到后的RDB文件载入自己的内存,然后主redis将缓冲区的内容在全部发送给从redis,之后的同步从服务器会发送一个offset的位置(等同于MySQL的binlog的位置)给主服务器,主服务器检查后位置没有错误将此位置之后的数据包括写在缓冲区的积压数据发送给redis从服务器,从服务器将主服务器发送的挤压数据写入内存,这样一次完整的数据同步,再之后再同步的时候从服务器只要发送当前的offset位 置给主服务器,然后主服务器根据响应的位置将之后的数据发送给从服务器保存到其内存即可。
另外redis的key 大小不建议超过2MB,因为2MB以下的key的性能是最佳的,key比较大的时候可以使用列表、key分级等方式将key进行拆分保存到不同的hash里面,另外第一次主从同步如果有数十G的数据,如果在主redis服务器写入频繁的时候同步,可能会出现RDB文件还没有向从服务器传输完成但主服务器的缓冲区已经写满导致从服务器发送sync的时候出现数据不一致导致再次全量同步的情况发生,因此建议在晚上住redis写入较少的时候在做同步,避免此类的情况发生

2.1:主从同步的一些概念:
redis使用异复制数据,从redis 2.8开始,从服务器会以每秒一次的频率向主服务器报告复制流(replication stream)的处理进度
一个主服务器可以同时有多个从服务器
从服务器还可以有自己的从服务器
复制功能不会阻塞从服务器,只要在redis.conf中做了设置,及时从服务器正在和主服务器进行初次同步,从服务器也可以使用之前的数据类处理客户端请求,不过从服务器删除就版本数据并载入新数据的时间段内,连接请求会被阻塞,可以设置从服务器让他在和主服务器之间断开连接时,向客户端发送一个错误信息
复制功能用于实现数据冗余,可以在从服务器执行复杂的命令在从服务器执行,以减轻主服务器的压力
可以在从服务器执行数据持久化而关闭主服务器的持久化功能,然后在从服务器执行数据持久化即可
2.2:是否需要在主服务器打开持久化功能:
有一个场景,有三个服务器,主服务器,从服务器A和从服务器B,从服务器A从主服务器同步数据,而从服务器B从服务器A同步数据,假如主服务器没有开启数据持久化,那么当主服务器出现问题重启redis服务后由于没有做数据持久会丢失所以数据为空,而从服务器A同步主服务器后也没有数据,从服务器B同步从服务器A的数据之后也会没有数据,因此主服务器还是推荐开启数据持久化以避免产生一系列的问题。
另外
2.3:配置redis主从同步,在另外一台服务器安装同版本的redis,并执行以下操作:
2.3.1:在从服务器进行操作:

[root@node1 src]# redis-cli -h 192.168.3.13
192.168.3.13:6379> SLAVEOF 192.168.3.199 6379 # 配置3.199为主服务器
OK

2.3.2:查看redis从服务器的状态:
192.168.3.13:6379> info

#其他信息省略
# Replication
role:slave # 状态为从服务器
master_host:192.168.3.199 # 主服务器的IP和端口信息
master_port:6379
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:15
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

192.168.3.13:6379> set key001 value001 #从服务器默认为read only即只读模式,无法写入数据
(error) READONLY You can't write against a read only slave

2.3.3:在redis主服务器执行INFO查看服务器信息:

# Replication
#其他信息省略
role:master #状态为主服务器
connected_slaves:1 #已经有一个从服务器连接
slave0:ip=192.168.3.13,port=6379,state=online,offset=477,lag=1
master_repl_offset:477
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:476

2.3.4:redis主从之间的心跳检测,可以通过MONITOR命令查看:

192.168.3.13:6379> MONITOR
OK
1499680760.936254 [0 192.168.3.199:6379] "PING"
1499680770.341823 [0 192.168.3.199:6379] "PING"
1499680779.735807 [0 192.168.3.199:6379] "PING"
1499680789.117626 [0 192.168.3.199:6379] "PING"

2.4:将从服务器设置为主服务器:

192.168.3.13:6379> SLAVEOF no one #将从服务器切换为主服务器
OK
192.168.3.13:6379> info
# 其他信息省略
# Replication
role:master #即切换为主服务器
connected_slaves:0
master_repl_offset:981
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
原文地址:https://www.cnblogs.com/reblue520/p/7327335.html