1-Redis5-持久化篇

about

所谓持久化,就是将内存中的数据刷写到本地,达到持久化和数据恢复的目的。

在Redis中,有RDB快照(snapshotting)和AOF(appendonly-file)两种持久化方式。
我们分别来看看。

RDB

想要触发持久化的动作有两种方式,分别是手动触发和自动触发。

但无论如何触发,都是以覆盖写的形式写入到同一份RDB文件中,即RDB的形式,持久化文件都只有一份,比较方便管理。

手动触发持久化

手动触发这里介绍两个命令:

  • save,同步命令,也就是该命令会占用Redis的主进程,在save命令执行期间,Redis将会阻塞所有的客户端请求,所以,当数据量非常大使,不推荐使用该命令。
  • bgsave,异步命令,Redis使用Linux的fork()生成一个子进程来做持久化的工作,而主进程则继续提供其他服务。

手动触发这里只需要在配置文件中配置:

dir "/data/redis6379/"   # 持久化文件保存的目录,目录位置可以更改为其它目录
dbfilename redis.rdb   	 # 持久化文件名,可以带端口号也可以不带,文件名也可以随意,只要是.rdb结尾就行


# 执行命令
mkdir -p /data/redis6379/
chmod 777 /data/redis6379
vim /opt/redis6379/conf/redis6379.conf

# 添加如下配置
dir "/data/redis6379/"
dbfilename redis.rdb

完事重启Redis服务,就可以在线执行持久化操作了:

[root@cs ~]# systemctl restart redis
[root@cs ~]# redis-cli 
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> SAVE
OK
127.0.0.1:6379> BGSAVE
Background saving started
127.0.0.1:6379> 
127.0.0.1:6379> 
[root@cs ~]# ls /data/redis6379/
redis.rdb

模拟上面图中的操作:

redis-cli set k1 v1
redis-cli set k2 v2
redis-cli set k3 v3
redis-cli SAVE
redis-cli set k4 v4
redis-cli set k5 v5
redis-cli keys \*
pkill -9 redis
systemctl start redis
redis-cli keys \*

[root@cs ~]# redis-cli set k1 v1
OK
[root@cs ~]# redis-cli set k2 v2
OK
[root@cs ~]# redis-cli set k3 v3
OK
[root@cs ~]# redis-cli SAVE
OK
[root@cs ~]# redis-cli set k4 v4
OK
[root@cs ~]# redis-cli set k5 v5
OK
[root@cs ~]# redis-cli keys \*
1) "k1"
2) "k4"
3) "k2"
4) "k5"
5) "k3"
[root@cs ~]# pkill -9 redis
[root@cs ~]# systemctl start redis
[root@cs ~]# redis-cli keys \*
1) "k2"
2) "k3"
3) "k1"

果然,丢了两条数据。

savebgsave对比:

命令 save bgsave
I/O类型 同步 异步
是否阻塞 是(阻塞发生在fock()阶段,但通常非常快)
复杂度 O(n) O(n)
优点 不会消耗额外的内存 不阻塞客户端命令
缺点 阻塞客户端命令 需要fork子进程,消耗额外内存

当然了,生产中一般不会使用上述两个命令,而是使用自动触发机制。

自动触发持久化

自动触发需要在配置文件中配置相关触发规则。

# RDB 自动持久化规则,当满足一下任意一个条件时,自动触发bgsave进行持久化
# 当然,你也可以自定义其它规则,比如 save 30 100 
# 当 900 秒内至少有 1 个key 被改动时,自动执行持久化操作
save 900 1

# 当 300 秒内至少有 10 个key 被改动时,自动执行持久化操作
save 300 10

# 当 60 秒内至少有 10000 个key被改动时,自动执行持久化操作
save 60 10000

# 数据持久化文件存储目录
dir "/data/redis6379/"

# RDB持久化文件名
dbfilename redis.rdb       # 持久化文件名,可以带端口号也可以不带,文件名也可以随意,只要是.rdb结尾就行

# bgsave过程中发生错误时,是否停止写入,通常为 yes
rdbcompression yes

# 是否对RDB文件进行校验,通常为 yes
rdbchecksum yes

# 最终拷贝这些命令到配置文件
vim /opt/redis6379/conf/redis6379.conf

dir "/data/redis6379/"
dbfilename redis.rdb
save 900 1
save 300 10
save 60 10000
rdbcompression yes
rdbchecksum yes

# 重启redis
systemctl restart redis

shutdown实验

来观察一个现象。

在我配置了自动化触发持久化的配置之后,配置规则是默认的:

save 900 1
save 300 10
save 60 10000

然后进行重启redis,并且设置一个key,此时redis中应该只有一个key,不符合bgsave的任何一个条件,也就是不会进行持久化,然后立马关闭Redis服务,按道理说,重启Redis后,数据应该是丢失的,但演示效果并没有丢失,如下:

redis-cli set k1 v1
redis-cli keys \*
redis-cli SHUTDOWN
systemctl start redis
redis-cli keys \*

[root@cs ~]# redis-cli set k1 v1
OK
[root@cs ~]# redis-cli keys \*
1) "k1"
[root@cs ~]# redis-cli SHUTDOWN
[root@cs ~]# systemctl start redis
[root@cs ~]# redis-cli keys \*		# 数据没有丢失为什么?
1) "k1"
[root@cs ~]# 

其原因就是,你用下面三种方式关闭Redis服务,都会在进程退出之前,自动执行一次bgsave之后,再退出,这就是数据没有丢失的原因。

SHUTDOWN
kill 2490		# 2490是redis进程id
pkill redis

另外,当只配置了RDB的持久化时,重启Redis服务时,会自动读取RDB文件进行数据恢复。

小结
RDB的优点:

  • RDB是一个非常紧凑的文件,它保存了某个时间点得数据集,非常适用于数据集的备份,比如你可以在每个小时报保存一下过去24小时内的数据,同时每天保存过去30天的数据,这样即使出了问题你也可以根据需求恢复到不同版本的数据集。
  • RDB是一个紧凑的单一文件,很方便传送到另一个远端数据中心,非常适用于灾难恢复。
  • RDB在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他IO操作,所以RDB持久化方式可以最大化redis的性能。
  • 与AOF相比,在恢复大的数据集的时候,RDB方式会更快一些。

RDB缺点:

  • 耗时、耗性能。RDB 需要经常fork子进程来保存数据集到硬盘上,当数据集比较大的时候,fork的过程是非常耗时的,可能会导致Redis在一些毫秒级内不能响应客户端的请求。如果数据集巨大并且CPU性能不是很好的情况下,这种情况会持续1秒,AOF也需要fork,但是你可以调节重写日志文件的频率来提高数据集的耐久度。
  • 不可控、丢失数据。如果你希望在redis意外停止工作(例如电源中断)的情况下丢失的数据最少的话,那么RDB不适合你。虽然你可以配置不同的save时间点(例如每隔5分钟并且对数据集有100个写的操作),是Redis要完整的保存整个数据集是一个比较繁重的工作,你通常会每隔5分钟或者更久做一次完整的保存,万一在Redis意外宕机,你可能会丢失几分钟的数据。

-9的血泪教训

在生产中使用redis,不要用-9参数,这句话你要牢记。

我们做个实验。

首先我只配置了rdb的持久化规则。并且redis此时的数据是空的。然后我们往redis中添加一些数据,但不触发rdb的持久化规则,就直接pkill -9干掉redis!

问:当redis重新启动后,这些数据还在吗?

redis-cli flushall
redis-cli dbsize
for i in {1..1000}; do redis-cli set zhangkai$i $i;echo $i;done
redis-cli dbsize
pkill -9 redis
systemctl start redis
redis-cli dbsize


[root@cs ~]# redis-cli flushall
OK
[root@cs ~]# redis-cli dbsize
(integer) 0
[root@cs ~]# for i in {1..1000}; do redis-cli set zhangkai$i $i;echo $i;done
.....输出太多,这里省略......
[root@cs ~]# redis-cli dbsize
(integer) 1000
[root@cs ~]# pkill -9 redis
[root@cs ~]# systemctl start redis
[root@cs ~]# redis-cli dbsize
(integer) 0

答案是不在了.......

关于kill/pkill/pkill -9/shutdown的补充

kill 是指定进程ID杀死进程,其实默认参数是发送-15信号,即通知进程退出,进程在退出之前可以清理并释放资源。
pkill 是指定进程名杀死进程,也是默认发送发送-15信号,即通知进程退出,进程在退出之前可以清理并释放资源。
而-9参数则是直接强制杀死进程。

那么在redis中,你可以通过kill和pkill或者shutdown来关闭redis,那么背后其实都是通知进程退出,而退出前,redis则是会自动执行下bgsave,拍个快照保存数据到本地。

如果是用pkill -9 redis则是直接干掉了redis进程,不会触发bgsave命令,那么丢数据也就不足为奇了。

所以,求你了,在生产中使用redis,不要用-9参数。

AOF

RDB持久化并不是完美的,如果Redis因为某些原因造成了宕机,那么将会丢失最近写入、但并未保存到快照中的数据。
在Redis1.1开始,增加了AOF来补充RDB的不足。
AOF持久化的工作机制是,每当Redis执行修改数据集的命令时,这个命令就会被追加到AOF文件的末尾。数据恢复时重放这个AOF文件即可恢复数据。

主要参数:

# 是否开启aof,默认是no
appendonly no

# 触发持久化的条件
appendfsync always/everyesc/no

其中:

  • appendonly:是(yes)否(no)开启AOF持久化,默认no
  • appendsync:触发持久化的条件,有下面三种选项:
    • always:每当修改数据集都会记录,慢但非常安全。
    • everysec:每秒钟fync一次,足够快(和使用 RDB 持久化差不多),就算有故障时也只会丢失 1 秒钟的数据。推荐(并且也是默认)使用该策略, 这种 fsync 策略可以兼顾速度和安全性。
    • no:由操作系统来决定什么时候同步数据,这个选项不常用。

AOF重写机制
因为AOF会一直将命令追加到文件末尾,导致该文件的体积会越来越大,而且它会保存一些重复性的命令,而这些重复性的命令可以用一条或者极少的命令就能替代....
为了优化这种情况,Redis支持:在不妨碍正常处理客户端请求中,对AOF文件进行重建(rebuild),也就是执行bgrewriteaof命令,会生成一个新的AOF文件,这个文件包含重建当前数据集所需要的最少命令。
在Redis2.2版本之前,需要手动执行bgrewriteaof命令,该命令会异步执行一个AOF文件重写操作,重写时会创建一个当前AOF文件的优化版本,即使bgrewriteaof执行失败,也不会有任何数据丢失,因为旧的AOF文件在bgrewriteaof执行成功之前不会被修改和覆盖。
到了Redis2.4则开始支持配置自动触发AOF重写机制了。
AOF重写机制的特点:减少AOF文件对磁盘的空间占用;加速数据恢复。

为啥要重写:

redis-cli	 	aof记录	 	   redis⾥的数据
set k1 v1 		set k1 k1		k1/v1

set k2 v2 		set k1 v1		k1/v1
		 		set k2 v2		k2/v2

set k3 v3 		set k1 v1		k1/v1
		 		set k2 v2		k2/v2
		 		set k3 v3 		k3/v3

del k1			set k1 v1		k2/v2
				set k2 v2		k3/v3
				set k3 v3
				del k1

del k2			set k1 v1		k3/v3
				set k2 v2
				set k3 v3
				del k1
				del k2

问题来了,此时aof中,有意义的记录只有一条:
				set k3 v3

所以,对于aof文件特别大的话, 类似于这种情况会很多,所以要进行重写,把无效的命令精简掉。

其它参数:

# 是否开启aof,默认是no
appendonly no

# 触发持久化的条件
appendfsync always/everyesc/no

# 数据持久化文件存储目录,如果单独使用aof,那么配置项就需要加上dir,如果同时使用了rdb,有了dir参数,aof这里则直接指定文件名即可
dir "/data/redis_data/6379"

# 是否在执行重写时不同步数据到AOF文件
# 这里的 yes,就是执行重写时不同步数据到AOF文件
no-appendfsync-on-rewrite yes

# 触发AOF文件执行重写的最小尺寸,如果将来真的用这个参数,且重度使用redis,则这个64兆就太小了,你可以调整以G为单位
auto-aof-rewrite-min-size 64mb

# 触发AOF文件执行重写的增长率
auto-aof-rewrite-percentage 100


# aof文件保存位置,
# dir "/data/redis6379/"
appendfilename "redis.aof"		



# 最终拷贝这些命令到配置文件
vim /opt/redis6379/conf/redis6379.conf

appendonly yes
appendfsync everysec
appendfilename "redis.aof"	
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
no-appendfsync-on-rewrite yes

# 重启redis
systemctl restart redis

关于AOF重写,当AOF文件的体积大于64Mb,并且AOF文件的体积比上一次重写之后的体积大了至少一倍(100%)时,Redis将执行 bgrewriteaof 命令进行重写,当然这个命令也能手动执行。

aof和rdb的优先级

问题来了:aof和rdb同时存在,redis重启会读取哪⼀个数据?

来个实验,首先,我这里同时开启aof和rdb持久化,并且运行正常,且redis此时没有key。

ls /data/redis6379/
redis-cli keys \*


[root@cs ~]# ls /data/redis6379/
redis.aof  redis.rdb
[root@cs ~]# redis-cli keys \*
(empty list or set)

1. 设置k1/k2两个key,并把它保存到rdb文件中

设置两个key,并且立即进行bgsave,此时这k1和k2这两个key会被保存到rdb文件中;

紧接着退出客户端,并且将rdb文件暂时移走。

redis-cli mset k1 v1 k2 v2
redis-cli keys \*
redis-cli BGSAVE
mv /data/redis6379/redis.rdb /data/
ls /data/redis6379/


[root@cs ~]# redis-cli mset k1 v1 k2 v2
OK
[root@cs ~]# redis-cli keys \*
1) "k2"
2) "k1"
[root@cs ~]# redis-cli BGSAVE
Background saving started
[root@cs ~]# mv /data/redis6379/redis.rdb /data/
[root@cs ~]# ls /data/redis6379/
redis.aof

2. 设置k3/k4两个key,并把它保存到aof文件中

接下来,执行先清空所有key,然后重新设置k3和k4这两个key,这两个key会被自动保存到aof文件中。

在把aof也挪走,接着停止redis,删除掉原有的rdb文件。

然后再把之前挪走的aof和rdb文件挪回去,现在,rdb文件中有k1和k2两个key;aof中有k3和k4两个key。

我们再启动redis之后,就知道redis启动过程中读取的是哪个文件进行数据恢复的了。

redis-cli keys \*
redis-cli FLUSHALL
redis-cli keys \*
redis-cli mset k3 v3 k4 v4
mv /data/redis6379/redis.aof /data/
redis-cli shutdown
ls /data/redis6379/
rm -rf /data/redis6379/*
mv /data/redis.aof /data/redis6379/
mv /data/redis.rdb /data/redis6379/
systemctl start redis
redis-cli keys \*

[root@cs ~]# redis-cli keys \*
1) "k2"
2) "k1"
[root@cs ~]# redis-cli FLUSHALL
OK
[root@cs ~]# redis-cli keys \*
(empty list or set)
[root@cs ~]# redis-cli mset k3 v3 k4 v4
OK
[root@cs ~]# mv /data/redis6379/redis.aof /data/
[root@cs ~]# redis-cli shutdown
[root@cs ~]# ls /data/redis6379/
redis.rdb
[root@cs ~]# rm -rf /data/redis6379/*
[root@cs ~]# mv /data/redis.aof /data/redis6379/
[root@cs ~]# mv /data/redis.rdb /data/redis6379/
[root@cs ~]# systemctl start redis
[root@cs ~]# redis-cli keys \*
1) "k4"
2) "k3"
[root@cs ~]# 

结论有了,当aof和rdb同时存在的时候,redis会优先读取aof的数据进行恢复。

pkill -9实验

在rdb部分,我们做了一个pkill -9的实验,结果发现rdb会丢数据,那么aof会吗?我们来做一个同样的实验。

首先,我配置好了aof和rdb两种持久化的方式,并且redis数据是为空的。

appendonly yes
appendfsync everysec
appendfilename redis.aof
no-appendfsync-on-rewrite yes
auto-aof-rewrite-min-size 64mb
auto-aof-rewrite-percentage 100

我将启动两个终端,一个终端进行往redis中set数据,另一个终端,在第一个终端set数据时,进行pkill -9杀掉redis,我们来观察redis重新启动后,数据有没有丢失。

终端1,造数据。

redis-cli flushall
redis-cli dbsize
for i in {1..1000}; do redis-cli set zhangkai$i $i;echo $i;done


[root@cs ~]# redis-cli flushall
OK
[root@cs ~]# redis-cli dbsize
(integer) 0
[root@cs ~]# for i in {1..1000}; do redis-cli set zhangkai$i $i;echo $i;done
OK
1
OK
2
OK
.....
OK
222
OK
223
Could not connect to Redis at 127.0.0.1:6379: Connection refused
224
Could not connect to Redis at 127.0.0.1:6379: Connection refused
225

终端2,杀掉redis,并重新启动Redis,检查数据是否还在。

pkill -9 redis
systemctl start redis
redis-cli dbsize
redis-cli get zhangkai223

[root@cs ~]# pkill -9 redis
[root@cs ~]# systemctl start redis
[root@cs ~]# redis-cli dbsize
(integer) 223
[root@cs ~]# redis-cli get zhangkai223
"223"
[root@cs ~]# 

结论,aof相对⽐较安全,最多丢失1秒数据。

aof中设置了过期时间的key,Redis重启会如何处理?

想一想这个问题:aof中设置了过期时间的key,在没有过期时,我们Redis意外宕机了,然后手动重启Redis之后,会如何处理?

redis-cli flushall
redis-cli setex k1 20 v1
redis-cli ttl k1
redis-cli ttl k1
redis-cli shutdown
cat /data/redis6379/redis.aof

[root@cs ~]# redis-cli flushall
OK
[root@cs ~]# redis-cli setex k1 20 v1
OK
[root@cs ~]# redis-cli ttl k1
(integer) 16
[root@cs ~]# redis-cli ttl k1
(integer) 15
[root@cs ~]# redis-cli shutdown
[root@cs ~]# cat /data/redis6379/redis.aof   # 不用太懂这个aof文件,虽然它允许我们手动修改,但我们通常不会修改它
SET							# 整个意思就是设置一个key,并指定过期时间
$2
k1
$2
v1
*3
$9
PEXPIREAT
$2
k1
$13
1690978804733				# 这是一个Unix时间戳,表示在这个时间戳时,该k1过期

然后我们过了一段时间,肯定超过了过期时间20秒,再启动Redis观察:

[root@cs ~]# systemctl start redis
[root@cs ~]# redis-cli ttl k1
(integer) -2

返回-2表示这个key不存在,说明Redis启动过程中,再根据aof文件恢复数据时,遇到过期的key,会校验一下是否过期,如果过期了,就按照过期处理了。

AOF的优点

  • 使用AOF 会让你的Redis更加耐久: 你可以使用不同的fsync策略:每次写的时候fsync。使用默认的每秒fsync策略,Redis的性能依然很好(fsync是由后台线程进行处理的,主线程会尽力处理客户端请求),一旦出现故障,你最多丢失1秒的数据。
  • AOF文件是一个只进行追加的日志文件,即使由于某些原因(磁盘空间已满,写的过程中宕机等等)未执行完整的写入命令,你也也可使用redis-check-aof工具修复这些问题。
  • Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写: 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。 整个重写操作是绝对安全的,因为 Redis 在创建新 AOF 文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。 而一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。
    AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松。 导出(export) AOF 文件也非常简单: 举个例子, 如果你不小心执行了 FLUSHALL 命令, 但只要 AOF 文件未被重写, 那么只要停止服务器, 移除 AOF 文件末尾的 FLUSHALL 命令, 并重启 Redis , 就可以将数据集恢复到 FLUSHALL 执行之前的状态。

AOF的缺点

  • 对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。

rdb和aof用哪个?

RDB和AOF对比:

选项 RDB AOF
数据恢复优先级
体积
恢复速度
数据安全性 丢数据 根据策略决定

那么aof和rdb这两种持久化方式,在生产中,该怎么用呢?

  • 官方推荐两个都启用,如果是主从环境,可以主库只开启aof,从库只开rdb做备份。
  • 如果对数据不敏感,可以选单独用RDB。
  • 不建议单独用 AOF,因为可能会出现Bug。
  • 如果只是做纯内存缓存,可以都不用。

性能建议:

  • 因为RDB文件通常只用作后备用途,如果是主从环境,建议只在Slave上持久化RDB文件,而且只要15分钟备份一次就够了,只保留save 900 1这条规则。
  • 代价,一是带来了持续的IO,二是AOF rewrite的最后将rewrite过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。
  • 只要硬盘许可,应该尽量减少AOF rewrite的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上。
  • 默认超过原大小100%大小时重写可以改到适当的数值。

最后,如果同时配置了RDB和AOF,那么在数据恢复时,Redis会优先选择AOF文件进行数据恢复,如果你就想使用RDB恢复怎么办?可以在配置文件中先把AOF关闭,然后重启Redis,恢复完数据,在线开启AOF即可,我们后续有个这样的案例。

备份与恢复

Redis的备份也比较简单,我们这里需要两台服务器,并且同样的都是安装了Redis,且都只配置了rdb持久化,并没有配置aof。

两台服务器:

  • db01:192.168.10.150,生成备份(可以是rdb或者aof)文件,然后将备份文件拷贝到db02上。
  • db02:192.168.10.151,启动Redis根据传来的备份文件进行数据恢复。

这里呢,先要准备另一台服务器,并且两台服务器之前要能进行数据交互。

快速部署第二台服务器

我的第二台服务器仍然和第一台服务器配置一样,同样是centos7系统。

那么你可以这么做,直接将第一台服务器的Redis直接拷贝一份,然后稍作更改即可使用。

因为需要两台Linux服务器之间进行数据交互,所以要先配置下免密登录。

此时,你的db02服务器处于启动状态,并且关闭了防火墙。

在db02上操作:

# 关闭防火墙和下载一些可能用到的工具
systemctl stop firewalld.service
systemctl disable firewalld.service
systemctl status firewalld.service
sed -i.ori 's#SELINUX=enforcing#SELINUX=disabled#g' /etc/selinux/config
yum update -y
yum -y install gcc automake autoconf libtool make
yum -y install net-tools vim wget lrzsz

# 生成类型rsa免密的公钥私钥,下面的命令执行完,一路回车即可
ssh-keygen

[root@cs ~]# ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): 
Created directory '/root/.ssh'.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:jRH3eoOMBgLjk4JT5ii5YluscvuSHg8+/dPVSOCLOo8 root@cs
The key's randomart image is:
+---[RSA 2048]----+
|  =     . .      |
|.B +    .o .     |
|B = . ....  .    |
|.+.. . ..*.o     |
|o. o   .So=oo    |
|o +   ... o...   |
|.o+o . . .       |
|.o+=+.. .        |
| .++E+o.         |
+----[SHA256]-----+


# 将公钥拷贝到目标机器上
ssh-copy-id 目标ip

ssh-copy-id 192.168.10.150

[root@cs ~]# ssh-copy-id 192.168.10.150
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/root/.ssh/id_rsa.pub"
The authenticity of host '192.168.10.150 (192.168.10.150)' can't be established.
ECDSA key fingerprint is SHA256:IpkQ09hlQlGw6zgKwI8tVRbme+jRTATj6vnOv9a1JaQ.
ECDSA key fingerprint is MD5:7d:8b:62:ca:c2:d0:6a:f5:d0:06:08:22:22:b2:65:d0.
Are you sure you want to continue connecting (yes/no)? yes					# 提示这里输入yes
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
root@192.168.10.150's password:   				# 输入目标机器的登录密码

Number of key(s) added: 1

Now try logging into the machine, with:   "ssh '192.168.10.150'"
and check to make sure that only the key(s) you wanted were added.
[root@cs ~]# 

接下来,两台机器就可以无密码传输数据了。

这里用到了rsync工具,rsync(remote synchronize)是Liunx/Unix下的一个远程数据同步工具。

在db02上操作:

rsync -avz 192.168.10.150:/opt/redis* /opt/
rsync -avz 192.168.10.150:/usr/local/bin/redis* /usr/local/bin/
rsync -avz 192.168.10.150:/usr/lib/systemd/system/redis.service /usr/lib/systemd/system/
sed -i 's#150#151#g' /opt/redis6379/conf/redis6379.conf
mkdir -p /data/redis6379
# 添加用户报错也正常,因为我这台测试机器,添加过这个用户和组, 
# -u和-g选项表示同时添加具有特定UID和GID的用户
# -M创建一个没有主目录的用户
# -s表示当前创建的当前用户无法用来登录系统
# chown -R redis:redis表示指定目录以及内部的文件所有用户属组归于redis:redis
# groupdel redis
# cat /etc/group |grep redis
groupadd redis -g 1000
# userdel redis
useradd redis -u 1000 -g 1000 -M -s /sbin/nologin
chown -R redis:redis /opt/redis*
chown -R redis:redis /data/redis*
systemctl daemon-reload
systemctl start redis
redis-cli PING

应该都没问题了。

可能的报错汇总:

1.在db01上执⾏了命令
2.配置⽂件⾥的密码没删掉
3.配置⽂件⾥的重命名参数没删掉
4.⽤户id和组id冲突
5.没有rsync
6.拷⻉过来的配置⽂件没有修改IP地址

基于rdb备份与恢复

首先,必须保证db01和db02的redis都关闭了aof持久化,下面这两个命令在db01和db02上都执行下:

sed -i 's#appendonly yes#appendonly no#g' /opt/redis6379/conf/redis6379.conf
cat /opt/redis6379/conf/redis6379.conf

然后,再开始时吧。

db02先把自己的rdb清空,保持数据干净状态,db02操作:

systemctl stop redis
rm -rf /data/redis6379/*

[root@cs ~]# systemctl stop redis
[root@cs ~]# rm -rf /data/redis6379/*

接下来,db01进行操作,产生备份文件,并拷贝rdb文件到db02中。

systemctl stop redis
rm -rf /data/redis6379/*
systemctl start redis
redis-cli dbsize
for i in {1..10}; do redis-cli set zhangkai$i $i;echo $i;done
redis-cli dbsize
redis-cli bgsave
ll /data/redis6379/
scp -r /data/redis6379/redis.rdb root@192.168.10.151:/data/redis6379/


[root@cs ~]# systemctl stop redis
[root@cs ~]# rm -rf /data/redis6379/*
[root@cs ~]# rm -rf /data/redis6379/*
[root@cs ~]# systemctl start redis
[root@cs ~]# redis-cli dbsize
(integer) 0
[root@cs ~]# for i in {1..10}; do redis-cli set zhangkai$i $i;echo $i;done
OK
1
OK
....
ok
[root@cs ~]# redis-cli dbsize
(integer) 10
[root@cs ~]# redis-cli bgsave
Background saving started
[root@cs ~]# ls /data/redis6379/
redis.aof  redis.rdb
[root@cs ~]# ll /data/redis6379/
total 4
-rw-r--r-- 1 redis redis 228 Aug 17 11:34 redis.rdb
[root@cs ~]# scp -r /data/redis6379/redis.rdb root@192.168.10.151:/data/redis6379/
root@192.168.10.151's password: 
redis.rdb                                              100%  228   789.0KB/s   00:00    
[root@cs ~]# 

现在,db01中的rdb文件,顺利生成并拷贝到了db02中。

接下来,我们在db02中进行数据恢复的操作。

systemctl start redis
redis-cli dbsize
redis-cli keys \*

[root@cs ~]# systemctl start redis
[root@cs ~]# redis-cli dbsize
(integer) 10
[root@cs ~]# redis-cli keys \*
 1) "zhangkai9"
 2) "zhangkai3"
 3) "zhangkai8"
 4) "zhangkai7"
 5) "zhangkai2"
 6) "zhangkai5"
 7) "zhangkai4"
 8) "zhangkai6"
 9) "zhangkai1"
10) "zhangkai10"
[root@cs ~]# 

可以看到,顺利恢复了。

注意,在备份与恢复时,务必保证redis版本一致。

另外,RDB⾼版本兼容低版本,低版本不能兼容⾼版本。

3.x >> 5.X >> OK 
5.x >> 3.x >> No

混合模式如何进行备份恢复

有这样一个场景,比如从db01备份一份rdb文件,挪到db02上进行数据恢复。

本章所有的演示都在db02操作。

错误的恢复流程

首先,要做的事情,把现在有数据的rdb文件拷贝走一份,因为后续这个rdb文件会被破坏。

db02操作:

cp /data/redis6379/redis.rdb /data/
ll /data/redis6379
redis-cli keys \*

[root@cs ~]# ll /data/redis6379
total 4
-rw-r--r--. 1 root root 228 Aug 17 11:36 redis.rdb    # 注意此时的rdb文件是有数据的
[root@cs ~]# redis-cli keys \*
 1) "zhangkai9"
 2) "zhangkai3"
 3) "zhangkai8"
 4) "zhangkai7"
 5) "zhangkai2"
 6) "zhangkai5"
 7) "zhangkai4"
 8) "zhangkai6"
 9) "zhangkai1"
10) "zhangkai10"

如果此时,我们同时开启rdb和aof持久化之后,直接进行恢复的话,你会发现数据并没有恢复成功。因为aof比rdb的优先级高,Redis重启时,会优先读取aof文件,但此时的aof文件是空的,所以恢复失败。

我这里直接贴出db02的演示效果:

# 直接开启db02的aof
sed -i 's#appendonly no#appendonly yes#g' /opt/redis6379/conf/redis6379.conf
systemctl restart redis
redis-cli config get appendonly
ll /data/redis6379/
redis-cli dbsize



[root@cs ~]# sed -i 's#appendonly no#appendonly yes#g' /opt/redis6379/conf/redis6379.conf
[root@cs ~]# systemctl restart redis
[root@cs ~]# redis-cli config get appendonly
1) "appendonly"
2) "yes"
[root@cs ~]# ll /data/redis6379/
total 4
-rw-r--r--. 1 redis redis   0 Aug 17 11:53 redis.aof    # Redis启动是,创建了aof,但是空的
-rw-r--r--. 1 redis redis 228 Aug 17 11:53 redis.rdb    # rdb文件有数据(留意文件大小228),但并没有根据该文件恢复
[root@cs ~]# redis-cli dbsize							# 启动Redis后,发现数据并没有恢复,这符合上面说的情况:aof比rdb的优先级高,Redis重启时,会优先读取aof文件
(integer) 0


# 
#  
# 
[root@cs ~]# systemctl stop redis
[root@cs ~]# ll /data/redis6379/
total 4
-rw-r--r-- 1 redis redis  0 Aug  3 10:22 redis.aof
-rw-r--r-- 1 redis redis 92 Aug  3 10:28 redis.rdb   

问题来了:此时我们再做个重启Redis服务试试,看看会有什么现象:

systemctl restart redis
ll /data/redis6379/

[root@cs ~]# systemctl restart redis
[root@cs ~]# ll /data/redis6379/
total 4
-rw-r--r--. 1 redis redis  0 Aug 17 11:53 redis.aof
-rw-r--r--. 1 redis redis 92 Aug 17 11:58 redis.rdb

完了,芭比Q了!rdb这个有数据的文件也被清空了(原来228,现在92了),其原因就是正常停机会触发bgsave,但因为Redis中没数据,执行完bgsave,就把原来rdb文件重新覆盖了,所以,这个情况千万要注意,不想被覆盖,就用pkill -9直接干掉redis,不给bgsave的机会就好了。

所以,上面的恢复数据的方式是有问题。我来带你演示下正确的流程。

正确的恢复流程

正确的做法应该是这样的。

1. 关闭redis服务,关闭aof持久化,删掉持久化的rdb和aof文件

先停止Redis服务,并且修改配置文件,将appendonly yes,修改为appendonly no,其它参数暂时不用变,这样aof持久化就关闭了。

然后把aof和rdb文件都删掉吧,反正里面没数据,也不能用了。

再把原来备份到/data目录下的rdb文件拷贝回来。

systemctl stop redis
sed -i 's#appendonly yes#appendonly no#g' /opt/redis6379/conf/redis6379.conf
rm -rf /data/redis6379/*
cp /data/redis.rdb /data/redis6379/
ll /data/redis6379/

[root@cs ~]# systemctl stop redis
[root@cs ~]# sed -i 's#appendonly yes#appendonly no#g' /opt/redis6379/conf/redis6379.conf[root@cs ~]# cp /data/redis.rdb /data/redis6379/
[root@cs ~]# rm -rf /data/redis6379/*
[root@cs ~]# cp /data/redis.rdb /data/redis6379/
[root@cs ~]# ll /data/redis6379/
total 4
-rw-r--r--. 1 root root 228 Aug 17 12:03 redis.rdb    # rdb文件有数据了,我们数据恢复就靠它了

2. 开始进行基于rdb的数据恢复

systemctl start redis
redis-cli keys \*
redis-cli config get appendonly
ll /data/redis6379/

[root@cs ~]# systemctl start redis  		# 启动redis服务
[root@cs ~]# redis-cli keys \*				# 可以看到数据恢复了
 1) "zhangkai7"
 2) "zhangkai1"
 3) "zhangkai6"
 4) "zhangkai4"
 5) "zhangkai3"
 6) "zhangkai9"
 7) "zhangkai10"
 8) "zhangkai2"
 9) "zhangkai8"
10) "zhangkai5"
[root@cs ~]# redis-cli config get appendonly	# 但此时aof功能还没有开启
1) "appendonly"
2) "no"
[root@cs ~]# ll /data/redis6379/
total 4
-rw-r--r--. 1 root root 228 Aug 17 12:03 redis.rdb  # rdb文件还是好的

重点操作来了,我们如何不停机动态开启aof,并且将aof的配置动态更新到配置文件中?答案就是可以通过redis-cli进行动态的更新某个参数,并且将某个参数更新到配置文件中。

# 重点操作来了,我们如何不停机动态开启aof,并且将aof的配置动态更新到配置文件中?
[root@cs ~]# redis-cli
127.0.0.1:6379> DBSIZE						# 此时数据恢复了
(integer) 10
127.0.0.1:6379> CONFIG GET appendonly		# 但aof还是关闭的
1) "appendonly"
2) "no"
127.0.0.1:6379> CONFIG SET appendonly yes   # 动态开启它,但这只是临时生效,我们需要把这个状态更新到配置文件中
OK
127.0.0.1:6379> CONFIG GET appendonly		# aof开启了,但配置文件的该参数还是no状态
1) "appendonly"
2) "yes"
127.0.0.1:6379> CONFIG REWRITE				# 将最新的配置信息重写到配置文件中,是的真的写入成功了,不信你可以自己看下
OK
127.0.0.1:6379> exit
[root@cs ~]# systemctl restart redis		# 此时你关闭/重启Redis都是正常的了
[root@cs ~]# redis-cli dbsize
(integer) 10
[root@cs ~]# ll /data/redis6379/
total 8
-rw-r--r-- 1 redis redis 228 Aug  3 10:51 redis.aof		# aof文件内容也有的
-rw-r--r-- 1 redis redis 228 Aug  3 10:52 redis.rdb		# rdb也被覆盖成了最新的数据了

两种持久化的优缺点

RDB: 类似于快照,当前内存⾥的数据的状态持久化到硬盘
优点:压缩格式/恢复速度快
缺点:不是实时的,可能会丢数据,操作⽐较重量

AOF:类似于mysql的binlog,可以设置成每秒/每次操作都以追加的形式保存在⽇志⽂件中
优点:安全,最多只损失1秒的数据,具备⼀定的可读性
缺点:⽂件⽐较⼤,恢复速度慢

常见问题

save命令执行失败

centos7 + redis5.0.7

在持久化时,手动执行save命令执行失败:

观察日志,发现时save到文件时,没有权限:

既然是权限问题,就给权限就好了:

chmod 777 /data/redis6379

然后再执行save就好了。

posted @ 2023-08-06 07:21  听雨危楼  阅读(321)  评论(0编辑  收藏  举报