Redis 的数据全部在内存里,如果突然宕机,数据就会全部丢失,因此必须有一种机制来保证 Redis 的数据不会因为故障而丢失,这种机制就是 Redis 的持久化机制。
Redis为我们提供了持久化的机制,分别是RDB(Redis DataBase)和AOF(Append Only File)
1.RDB
(Redis DataBase)
是什么:
在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshbt快照,它恢复时是将快照文件直接读到内存里
Redis会单独创建(fork) 一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能。
如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。
redis.conf关于rdb的配置
save 900 1 //900秒一次
save 300 10 //300秒10次
save 60 10000 //60秒一万次
三者满足一个就会开始rdb快照存储
################################ SNAPSHOTTING ################################
#
# Save the DB on disk:
#
# save <seconds> <changes> //秒 次数
#
# Will save the DB if both the given number of seconds and the given
# number of write operations against the DB occurred.
#
# In the example below the behaviour will be to save:
# after 900 sec (15 min) if at least 1 key changed
# after 300 sec (5 min) if at least 10 keys changed
# after 60 sec if at least 10000 keys changed
#
# Note: you can disable saving completely by commenting out all "save" lines.
#
# It is also possible to remove all the previously configured save
# points by adding a save directive with a single empty string argument
# like in the following example:
#
# save ""
save 900 1 //900秒一次
save 300 10 //300秒10次
save 60 10000 //60秒一万次 三者满足一个就会开始rdb快照存储
dbfilename dump.rdb
是指定存储rdb文件的名称
dir ./
默认是在项目的启动目录下创建dump.rdb文件,例如在/usr/lhl/输入命令 /usr/lhl/redis-4.0.1/src/redis-server /usr/lhl/config/redis.conf 是在/usr/lhl/下创建dump.rdb文件,一般都是把redis-server redis.conf移动到同一目录下,一般都是/usr/local/bin下,看个人习惯(redis-server移动到任何目录下都能使用)
可以改成确定路径,编辑redis.conf 设置dir ldata/redis-40.1/这样启动服务时dump文件会固定在/data/redis-4.0.1且录下了,
# The filename where to dump the DB
dbfilename dump.rdb
# The working directory.
#
# The DB will be written inside this directory, with the filename specified
# above using the 'dbfilename' configuration directive.
#
# The Append Only File will also be created inside this directory.
#
# Note that you must specify a directory here, not a file name. 请注意,必须在此处指定目录,而不是文件名。
dir ./
操作实例:
这里100秒内进行7次操作,就会进行rdb快照存储
[root@C2_SW_5748F_1 copy]# ls //这里我把redis-server和redis-cli从/usr/lhl/redis-4.0.1/src/下复制过来了,因为我感觉这样方便,默认情况下dump.rdb也会创建在这个目录下
dump.rdb redis-cli redis.conf redis-server
[root@C2_SW_5748F_1 copy]# rm -f dump.rdb
[root@C2_SW_5748F_1 copy]# ls
redis-cli redis.conf redis-server
[root@C2_SW_5748F_1 copy]# redis-server redis.conf
9440:C 01 Apr 10:10:29.803 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
9440:C 01 Apr 10:10:29.803 # Redis version=4.0.1, bits=64, commit=00000000, modified=0, pid=9440, just started
9440:C 01 Apr 10:10:29.803 # Configuration loaded
[root@C2_SW_5748F_1 copy]# redis-cli -p 6379
127.0.0.1:6379> set a s
OK
127.0.0.1:6379> set s d
OK
127.0.0.1:6379> set d f
OK
127.0.0.1:6379> set f g
OK
127.0.0.1:6379> set g h
OK
127.0.0.1:6379> set h j
OK
127.0.0.1:6379> set j k
OK
127.0.0.1:6379> set k l
OK
127.0.0.1:6379> set o i // 开启线程2看查,已经创建出dump.rdb文件,在100秒以后
OK
127.0.0.1:6379> set p o
OK
127.0.0.1:6379> shutdown
not connected> exit
[root@C2_SW_5748F_1 copy]# ls
dump.rdb redis-cli redis.conf redis-server
[root@C2_SW_5748F_1 copy]# redis-server redis.conf
9491:C 01 Apr 10:13:58.579 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
9491:C 01 Apr 10:13:58.579 # Redis version=4.0.1, bits=64, commit=00000000, modified=0, pid=9491, just started
9491:C 01 Apr 10:13:58.579 # Configuration loaded
[root@C2_SW_5748F_1 copy]# redis-cli -p 6379
127.0.0.1:6379> keys *
1) "d"
2) "a"
3) "o"
4) "s"
5) "h"
6) "k"
7) "f"
8) "g"
9) "p"
10) "j"
127.0.0.1:6379> shutdown
not connected> exit
[root@C2_SW_5748F_1 copy]# ls
dump.rdb redis-cli redis.conf redis-server
[root@C2_SW_5748F_1 copy]# rm -f dump.rdb
[root@C2_SW_5748F_1 copy]# ls
redis-cli redis.conf redis-server
[root@C2_SW_5748F_1 copy]# redis-server redis.conf
9508:C 01 Apr 10:14:44.738 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
9508:C 01 Apr 10:14:44.738 # Redis version=4.0.1, bits=64, commit=00000000, modified=0, pid=9508, just started
9508:C 01 Apr 10:14:44.738 # Configuration loaded
[root@C2_SW_5748F_1 copy]# redis-cli -p 6379
127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379>
当然一般情况下都会拷贝dump.rdb文件到其他电脑上,万一这太电脑挂了,就凉凉了
子进程后台执行 RDB 持久化
为什么 Redis 使用子进程而不是线程来进行后台 RDB 持久化呢?主要是出于Redis性能的考虑,我们知道Redis对客户端响应请求的工作模型是单进程和单线程的,如果在主进程内启动一个线程,这样会造成对数据的竞争条件。所以为了避免使用锁降低性能,Redis选择启动新的子进程,独立拥有一份父进程的内存拷贝,以此为基础执行RDB持久化。
但是需要注意的是,fork 会消耗一定时间,fork时主进程会暂停只做复制操作,并且父子进程所占据的内存是相同的,当 Redis 键值较大时,fork 的时间会很长,这段时间内 Redis 是无法响应其他命令的。除此之外,Redis 占据的内存空间会翻倍。
生成 RDB 文件,并且持久化到硬盘
Redis 的 rdbSave 函数是真正进行 RDB 持久化的函数,它的大致流程如下:
- 首先打开一个临时文件,
- 调用 rdbSaveRio函数,将当前 Redis 的内存信息写入到这个临时文件中,
- 接着调用 fflush、fsync 和 fclose 接口将文件写入磁盘中,
- 使用 rename 将临时文件改名为 正式的 RDB 文件,
- 最后记录 dirty 和 lastsave等状态信息。这些状态信息在 serverCron时会使用到。
int rdbSave(char *filename, rdbSaveInfo *rsi) {
char tmpfile[256];
// 当前工作目录
char cwd[MAXPATHLEN];
FILE *fp;
rio rdb;
int error = 0;
/* 生成tmpfile文件名 temp-[pid].rdb */
snprintf(tmpfile,256,"temp-%d.rdb", (int) getpid());
/* 打开文件 */
fp = fopen(tmpfile,"w");
.....
/* 初始化rio结构 */
rioInitWithFile(&rdb,fp);
if (rdbSaveRio(&rdb,&error,RDB_SAVE_NONE,rsi) == C_ERR) {
errno = error;
goto werr;
}
if (fflush(fp) == EOF) goto werr;
if (fsync(fileno(fp)) == -1) goto werr;
if (fclose(fp) == EOF) goto werr;
/* 重新命名 rdb 文件,把之前临时的名称修改为正式的 rdb 文件名称 */
if (rename(tmpfile,filename) == -1) {
// 异常处理
....
}
// 写入完成,打印日志
serverLog(LL_NOTICE,"DB saved on disk");
// 清理数据保存记录
server.dirty = 0;
// 最后一次完成 SAVE 命令的时间
server.lastsave = time(NULL);
// 最后一次 bgsave 的状态置位 成功
server.lastbgsave_status = C_OK;
return C_OK;
....
}
关于SNAPSHOTTING快照
Save秒钟写操作次数
127.0.0.1:6379> set key01 123
OK
127.0.0.1:6379> save //save和bgsave立刻生成dump.rdb文件 ##shutdown也会立刻生成dump.rdb文件
OK
127.0.0.1:6379>
save时只管保存,其它不管,全部阻塞.
BGSAVE: Redlis会在后台异步进行快照操作,快照同时还可以响应客户端请求。可以通过lastsave命令获取最后一次成功执行快照的时间
执行flushall命令,也会产生dump.rdb文件,但里面是空的,无意义
Stop-writes-on-bgsave-error(默认为yes)
如果配置成no,表示你不在乎数据不一致或者有其他的手段发现和控制
rdbcompression:
对于存储到磁盘中的快照,可以设置是否进行压缩存储。如果是的话,redis会采用LZF算法进行压缩。如果你不想消耗CPU来进行压缩的话,可以设置为关闭此功能
rdbchecksum
在存储快照后,还可以让redis使用CRC64算法来进行数据校验,但是这样做会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能
如何恢复将备份文件
(dump.rdb)移动到redis 安装目录并启动服务即可 CONFIG GET dir获取目录
如何停止:
动态 所有停止RDB保存规则的方法: redis-cli config set save ""
优势:
适合大规模的数据恢复,对数据完整性和一致性要求不高
RDB是一个非常紧凑的文件,RDB在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他I0操作,所以RDB持久化方式可以最大化redis的性能.
与AOF相比,,在恢复大的数据集的时候,RDB方式会更快一些.
劣势:
在一定间隔时间做一次备份,所以如果redis意外down掉的话,就会丢失最后一次快照后的所有修改,Fork的时候,内存中的数据被克隆了一份,大致2倍的膨胀性需要考虑,当数据集比较大的时候,fork的过程是非常耗时的,,可能会导致Redis在一些毫秒级不能相应客户端请求
2、AOF
(Append Only File)
是什么
以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作
Redis.conf中AOF的配置
appendonly no(默认是关闭的)
必须开始appendonly no改成appendonly yes,才能用
appendfilename "appendonly.aof"
默认生成文件名称为appendonly.aof
############################## APPEND ONLY MODE ###############################
# By default Redis asynchronously dumps the dataset on disk. This mode is
# good enough in many applications, but an issue with the Redis process or
# a power outage may result into a few minutes of writes lost (depending on
# the configured save points).
#
# The Append Only File is an alternative persistence mode that provides
# much better durability. For instance using the default data fsync policy
# (see later in the config file) Redis can lose just one second of writes in a
# dramatic event like a server power outage, or a single write if something
# wrong with the Redis process itself happens, but the operating system is
# still running correctly.
#
# AOF and RDB persistence can be enabled at the same time without problems. //AOF和RDB持久性可以同时启用而不会出现问题
# If the AOF is enabled on startup Redis will load the AOF, that is the file
# with the better durability guarantees.
#
# Please check http://redis.io/topics/persistence for more information.
appendonly yes
# The name of the append only file (default: "appendonly.aof")
appendfilename "appendonly.aof"
生成appendonly.aof的操作
下面操作的思路为先删除dump.rdb文件,启动redis并连接上redis,查看数据时显示为empty,因为开启了aof,写入数据到redis中,退出redis,ls看查是否存在aof文件,删除dump.rdb文件确保访问数据库时得到的数据时通过aof进行持久化的,操作顺利完成,得到对应的数据
[root@C2_SW_5748F_1 copy]# ls
dump.rdb redis-cli redis.conf redis-server
[root@C2_SW_5748F_1 copy]# rm -f dump.rdb
[root@C2_SW_5748F_1 copy]# ls
redis-cli redis.conf redis-server
[root@C2_SW_5748F_1 copy]# redis-server redis.conf
[root@C2_SW_5748F_1 copy]# redis-cli -p 6379
127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> set k2 v2
OK
127.0.0.1:6379> shutdown
not connected> exit
[root@C2_SW_5748F_1 copy]# ls
appendonly.aof dump.rdb redis-cli redis.conf redis-server
[root@C2_SW_5748F_1 copy]# rm -f dump.rdb
[root@C2_SW_5748F_1 copy]# ls
appendonly.aof redis-cli redis.conf redis-server
[root@C2_SW_5748F_1 copy]# vim appendonly.aof
[root@C2_SW_5748F_1 copy]# redis-server redis.conf
[root@C2_SW_5748F_1 copy]# redis-cli -p 6379
127.0.0.1:6379> keys *
1) "k2"
2) "k1"
127.0.0.1:6379>
怎么修复损坏的appendonly.aof文件
因为修复redis-check-aof文件,所以把redis-check-aof文件移到到了当前文件夹下,默认是在src文件下的
[root@C2_SW_5748F_1 /]# mv /lhl/redis-4.0.1/src/redis-check-aof /lhl/copy/
[root@C2_SW_5748F_1 /]# cd /lhl/copy/
[root@C2_SW_5748F_1 copy]# ls
appendonly.aof dump.rdb redis-check-aof redis-cli redis.conf redis-server
故意损坏:wq!保存退出
再次连接时,发现无法连接
[root@C2_SW_5748F_1 copy]# vim appendonly.aof
[root@C2_SW_5748F_1 copy]# redis-server redis.conf
8021:C 02 Apr 00:44:08.041 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
8021:C 02 Apr 00:44:08.041 # Redis version=4.0.1, bits=64, commit=00000000, modified=0, pid=8021, just started
8021:C 02 Apr 00:44:08.041 # Configuration loaded
[root@C2_SW_5748F_1 copy]# ps -ef|grep redis
root 8035 7504 0 00:44 pts/0 00:00:00 grep --color=auto redis
[root@C2_SW_5748F_1 copy]# redis-cli -p 6379
Could not connect to Redis at 127.0.0.1:6379: Connection refused
Could not connect to Redis at 127.0.0.1:6379: Connection refused
not connected> exit
[root@C2_SW_5748F_1 copy]#
进行恢复appendonly.aof文件
[root@C2_SW_5748F_1 copy]# redis-check-aof --fix appendonly.aof
0x 51: Expected prefix '*', got: '1'
AOF analyzed: size=149, ok_up_to=81, diff=68
This will shrink the AOF from 149 bytes, with 68 bytes, to 81 bytes
Continue? [y/N]: y
Successfully truncated AOF
[root@C2_SW_5748F_1 copy]# redis-server redis.conf
8064:C 02 Apr 00:47:30.677 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
8064:C 02 Apr 00:47:30.677 # Redis version=4.0.1, bits=64, commit=00000000, modified=0, pid=8064, just started
8064:C 02 Apr 00:47:30.677 # Configuration loaded
[root@C2_SW_5748F_1 copy]# redis-cli -p 6379
127.0.0.1:6379> keys *
1) "k1"
2) "k2"
127.0.0.1:6379>
redis.conf文件关于aof的配置
appendfsync
- Always: 同步持久化每次发生数据变更会被立即记录到磁盘_性能较差但数据完整性比较好
- Everysec:出厂默认推荐,异步操作,每秒记录如果一秒内宕机, 有数据丢失
- No 不会记录到磁盘
# The default is "everysec", as that's usually the right compromise between
# speed and data safety. It's up to you to understand if you can relax this to
# "no" that will let the operating system flush the output buffer when
# it wants, for better performances (but if you can live with the idea of
# some data loss consider the default persistence mode that's snapshotting),
# or on the contrary, use "always" that's very slow but a bit safer than
# everysec.
#
# More details please check the following article:
# http://antirez.com/post/redis-persistence-demystified.html
#
# If unsure, use "everysec".
# appendfsync always
appendfsync everysec
# appendfsync no
No-appendfsync-on-rewrite: 重写时是否可以运用Appendfsync, 用默认no即可,保证数据安全性。
Rewrite
是什么:
AOF采用文件追加方式,文件会越来越大为避免出现此种情况,新增了重写机制,当AOF文件的大小超过所设定的阈值时,Redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集.可以使用命令bgrewriteaof
重写原理:
AOF文件持续增长而过大时,会fork出一条新进程来将文件重写(也是先写临时文件最后再rename),遍历新进程的内存中数据,每条记录有一条的Set语句。重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似
触发机制:
Redis会记录上次重写时的A0F大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发
# Automatic rewrite of the append only file.
# Redis is able to automatically rewrite the log file implicitly calling
# BGREWRITEAOF when the AOF log size grows by the specified percentage.
#
# This is how it works: Redis remembers the size of the AOF file after the
# latest rewrite (if no rewrite has happened since the restart, the size of
# the AOF at startup is used).
#
# This base size is compared to the current size. If the current size is
# bigger than the specified percentage, the rewrite is triggered. Also
# you need to specify a minimal size for the AOF file to be rewritten, this
# is useful to avoid rewriting the AOF file even if the percentage increase
# is reached but it is still pretty small.
#
# Specify a percentage of zero in order to disable the automatic AOF
# rewrite feature.
auto-aof-rewrite-percentage 100 //auto-aof-rewrite-min-size: 设置重写的基准值
auto-aof-rewrite-min-size 64mb //auto-aof-rewrite-percentage: 设置重写的基准值
优势:
存在三种同步方式:1、每秒同步2、每修改同步3、不同步
劣势:
相同数据集的数据而言aof文件要远大于rdb文件,恢复速度慢于rdb
Aof运行效率要慢于rdb,每秒同步策略效率较好,不同步效率和rdb相同
总结:
只做缓存:
如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式,
同时开启两种持久化方式:
在这种情况下,当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整.RDB的数据不实时,
同时使用两者时服务器重启也只会找AOF文件。
那要不要只使用AOF呢?
作者建议不要,因为RDB更适合用于备份数据库(AOF在不断变化不好备份),快速重启,而且不会有AOF可能潜在的bug,留着作为一个万一的手段。
性能建议
因为RDB文件只用作后备用途,建议只在Slave上持久化RDB文件,而且只要15分钟备份一次就够了,只保留save 900 1这条规则。
如果Enalbe AOF,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只load自己的AOF文件就可以了。代价一是带来了持续的IO,二是AOF rewrite的最后将rewrite过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少AOF rewrite的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上。默认超过原大小100%大小时重写可以改到适当的数值。
如果不Enable AOF,仅靠Master- Slave Replication实现高可用性也可以。能省掉一大 笔IO也减少了rewrite时带来的系统波动。代价是如果Master/Slave同时倒掉, 会丢失十几分钟的数据,启动脚本也要比较两Master/Slav中的RDB文件,载入较新的那个。