Redis6之(六)Redis持久化
一、持久化
持久化:就是将程序数据在持久状态和瞬时状态间转换的机制。即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。
持久化的功能:Redis是内存数据库,数据都是存储在内存中,为了避免进程退出导致数据的永久丢失,需要定期将Redis中的数据以某种形式(数据或命令)从内存保存到硬盘;当下次Redis重启时,利用持久化文件实现数据恢复。除此之外,为了进行灾难备份,可以将持久化文件拷贝到一个远程位置。
Redis持久化分为RDB持久化和AOF持久化:RDB将当前数据保存到硬盘,AOF则是将每次执行的写命令保存到硬盘。
由于AOF持久化的实时性更好,即当进程意外退出时丢失的数据更少,因此AOF是目前主流的持久化方式,不过RDB持久化仍然有其用武之地。
二、Redis持久化之RDB
2.1 RDB简介
2.1.1 RDB概述
RDB持久化就是在指定的时间间隔内将内存中的数据集快照写入磁盘
,也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里。当符合一定条件时Redis会自动将内存中的所有数据生成一份副本并存储在磁盘上,这个过程即为"快照"。
在Redis中,默认有RDB操作。
2.1.2 备份是如何执行的
备份的执行过程
Redis会单独创建一个子进程(fork)来进行持久化,会先将数据写入到一个
临时文件
中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件
。整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能。如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的备份过程
1.查询 rdb 文件的目录
2.将dump.rdb文件拷贝到别的地方
3.rdb的恢复 (rdb会启动自动恢复)
2.1.3 Fork
Fork的作用是复制一个与当前进程一样的进程。
新进程的所有数据(变量、环境变量、程序计数器等)数值和原进程一致,但是是一个全新的进程,并作为原进程的子进程
。
在Linux程序中,fork() 会产生一个和父进程完全相同的子进程,但子进程在此后多会 exec 系统调用,处于效率考虑,Linux中引入了"写时复制技术(Copy On Write)"
。
一般情况父进程和子进程会共用一段物理内存,只有进程空间的各段内容要发生变化时,才会将父进程的内容复制一份给子进程。
小贴士:
Copy On Write原理:
fork操作之后,内核会把父进程中所有的内存页都设为只读权限,然后将子进程的地址空间指向父进程。当其中某个进程写内存时,CPU检测到内存页是只读的,于是触发页异常中断,从而进入内核态。内核会把触发异常的页复制一份分配给写内存的进程,于是这个进程可以在复制的页上进行写操作,而不会更改共享的内存数据。
Copy On Write缺点:
如果在 fork 后父子进程都还需要继续进行写操作,那么会产生大量的页异常中断,造成内核态切换频繁,性能损失较大。
2.2 RDB持久化流程
2.3 如何触发RDB?
2.3.1 根据配置规则进程自动快照
Redis允许用户自定义快照条件,当符合快照条件时,Redis会自动执行快照操作
。进行快照的条件可以由用户在配置文件中自定义,由两个参数构成:时间窗口M和改动的键的个数N。每当时间M内被更改的键的个数大于N时,即符合自动快照条件。
例如:Redis安装目录中包含的配置文件样例中预设的3个条件。
设置场景:在20秒内至少有3个键被改变则进行快照。
2.3.2 用户执行 save或 bgsave命令
除了让Redis自动进行快照外,当进行服务重启、手动迁移以及备份时我们也会需要手动执行快照操作,Redis提供了两个命令来完成这一任务:
save命令
当执行 save命令时,Redis同步地进行快照操作,
在快照执行的过程中会阻塞所有来自客户端的请求
。当数据库中的数据比较多时,这一过程会导致 Redis 较长时间不响应,所以要尽量避免在生产环境中使用这一命令。bgsave命令
需要手动执行快照时推荐使用 bgsave命令。bgsave命令可以在后台异步地进行快照操作,快照的同时服务器还可以继续响应来自客户端的请求。
执行 bgsave后 Redis会立即返回 OK表示开始执行快照操作,如果想知道快照是否完成,可以通过 lastsave命令获取最近一次成功执行快照的时间,返回结果是一个Unix时间戳。
2.3.3 执行 flushall命令
当执行 flushall命令时,Redis 会清除数据库中的所有数据
。需要注意的是,不论清空数据库的过程是否触发了自动快照条件,只要自动快照条件不为空,Redis就会执行一次快照操作。
例如,当定义的快照条件为当1秒内修改10000个键时进行自动快照,而当数据库里只有一个键时,执行 flushall命令也会触发快照,即使这一过程实际上只有一个键被修改 了。当没有定义自动快照条件时,执行 flushall则不会进行快照。
2.3.4 执行复制时
当设置了主从模式时,Redis 会在复制初始化时进行自动快照。当使用复制操作时,即使没有定义自动快照条件,并且没有手动执行过快照操作,也会生成RDB快照文件。
2.4 RDB的优劣势
2.4.1 RDB的优势
- 适合大规模的数据恢复;
- 对数据完整性和一致性要求不高更适合使用;
- 节省磁盘空间;
- 恢复速度快。
2.4.2 RDB的劣势
- Fork的时候,内存中的数据被克隆了一份,大致2倍的膨胀性需要考虑;
- 虽然Redis在fork时使用了写时拷贝技术,但是如果数据庞大时还是比较消耗性能;
- 在备份周期在一定间隔时间做一次备份,所以如果Redis意外down掉的话,就会丢失最后一次快照后的所有修改。
2.5 如何停止RDB
动态停止RDB:
# save后给空值,表示禁用保存策略
redis-cli configset save""
2.6 RDB小总结
- RDB是一个非常紧凑的文件;
- RDB在保存RDB文件时父进程唯一需要做的就是 fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他IO操作,所以RDB持久化方式可以最大化Redis的性能;
- 与AOF相比,在恢复大的数据集的时候,RDB方式会更快一些;
- 但是数据丢失风险大;
- RDB小经常fork子进程来保存数据集到硬盘上,但数据集比较大的时候,fork的过程是非常耗时的,可能会导致Redis在一些毫秒级不能响应客户端的请求。
三、Redis持久化之AOF
3.1 AOF简介
AOF即Append Only File。
以日志的形式来记录每个写操作(增量保存)
,将Redis执行过的所有写指令
记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复文件。
当使用Redis存储非临时数据时,一般需要打开AOF持久化来降低进程中止导致的数据丢失。AOF可以将Redis执行的每一条写命令追加到硬盘文件中,这一过程显然会降低Redis的性能,但是大部分情况下这个影响是可以接受的,另外使用较快的硬盘可以提高AOF的性能。
3.2 AOF持久化流程
- 客户端的请求写命令会被 append追加到 AOF缓冲区内;
- AOF缓冲区根据AOF持久化策略(always,everysec,no)将操作 sync同步到磁盘的 AOF文件中;
- AOF文件大小超过重写策略或手动重写时,会对 AOF文件 rewrite重写,压缩 AOF文件;
- Redis服务器重启时,会重新 load加载 AOF文件中的写操作达到数据恢复的目的。
3.3 AOF启动/修复/恢复
3.3.1 开启AOF
AOF默认是不开启的
,可以在 redis.conf中配置文件名称,默认为 appendonly.aof,其文件的保存路径同 RDB的路径一致,都是用过dir参数设置的。
可以通过 appendonly参数启用:
开启 AOF持久化后每执行一条会更改 Redis中的数据的命令,Redis就会将该命令写入硬盘中的 AOF文件。
注意:
AOF和RDB同时开启,系统默认取AOF的数据(数据不会存在丢失)。
3.3.2 恢复AOF
正常恢复
- 修改默认的 appendonly no,改为yes;
- 将有数据的 aof文件复制一份保存到对应目录(查看目录:config get dir);
- 恢复:重启redis然后重新加载。
异常恢复
- 修改默认的appendonly no,改为yes;
- 如遇到AOF文件损坏,通过/usr/local/bin/redis-check-aof–fix appendonly.aof进行恢复;
- 备份被写坏的AOF文件;
- 恢复:重启redis,然后重新加载。
3.4 同步硬盘数据(同步频率设置)
虽然每次执行更改数据库内容的操作时,AOF都会将命令记录在AOF文件中,但是事实上,由于操作系统的缓存机制,数据并没有真正地写入硬盘,而是进入了系统的硬盘缓存
。 在默认情况下系统每30秒会执行一次同步操作,以便将硬盘缓存中的内容真正地写入硬盘, 在这30秒的过程中如果系统异常退出则会导致硬盘缓存中的数据丢失。
一般来讲启用AOF持久化的应用都无法容忍这样的损失,这就需要Redis在写入AOF文件后主动要求系统将缓存内容同步到硬盘中
。
在 Redis 中我们可以通过 appendfsync 参数设置同步的时机:
# 始终同步,每次 Redis的写入都会立刻记入日志,性能较差但数据完整性比较好
appendfsync always# 每秒同步,每秒记入日志一次,如果宕机,本秒的数据可能丢失
appendfsync everysec# redis不主动进行同步,把同步时机交给操作系统
appendfsync no
- 默认情况下Redis采用everysec规则,即每秒执行一次同步操作;
- always表示每次执行写 入都会执行同步,这是最安全也是最慢的方式;
- no表示不主动进行同步操作,而是完全交由操作系统来做(即每30秒一次),这是最快但最不安全的方式。
一般情况下使用默认值 everysec就足够了,既兼顾了性能又保证了安全。 Redis 允许同时开启 AOF 和 RDB,既保证了数据安全又使得进行备份等操作十分容易。 此时重新启动 Redis后 Redis会使用 AOF文件来恢复数据,因为 AOF方式的持久化可能丢失的 数据更少。
3.5 Rewrite压缩
3.5.1 什么是Rewrite压缩
AOF采用文件追加方式,文件会越来越大为避免出现此种情况,新增了重写机制,当AOF文件的大小超过所设定的阈值时,Redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集
。可以使用命令 bgrewriteaof
3.5.2 重写原理,如何实现重写
重写原理
AOF文件持续增长而过大时,会 fork出一条新进程来将文件重写(也是先写临时文件最后再rename),
redis4.0版本后的重写,是指上就是把 rdb 的快照,以二级制的形式附在新的 aof 头部,作为已有的历史数据,替换掉原来的流水账操作。
no-appendfsync-on-rewrite:yes/no
- 如果 no-appendfsync-on-rewrite=yes ,不写入 aof文件只写入缓存,用户请求不会阻塞,但是在这段时间如果宕机会丢失这段时间的缓存数据。(降低数据安全性,提高性能);
- 如果 no-appendfsync-on-rewrite=no, 还是会把数据往磁盘里刷,但是遇到重写操作,可能会发生阻塞。(数据安全,但是性能降低)。
触发机制,何时重写
Redis会记录上次重写时的 AOF大小,默认配置是当 AOF文件大小是上次 rewrite后大小的一倍且文件大于64M时触发。
注意:重写虽然可以节约大量磁盘空间,减少恢复时间。但是每次重写还是有一定的负担的,因此设定Redis要满足一定条件才会进行重写。
auto-aof-rewrite-percentage:设置重写的基准值,文件达到100%时开始重写(文件是原来重写后文件的2倍时触发);
auto-aof-rewrite-min-size:设置重写的基准值,最小文件64MB。达到这个值开始重写;
例如:文件达到70MB开始重写,降到50MB,下次什么时候开始重写?100MB
系统载入时或者上次重写完毕时,Redis会记录此时AOF大小,设为 base_size,如果Redis的AOF当前大小 >= base_size +base_size*100% (默认)且当前大小>=64mb(默认)的情况下,Redis会对AOF进行重写。
3.5.3 重写流程
- bgrewriteaof触发重写,判断是否当前有 bgsave或 bgrewriteaof在运行,如果有,则等待该命令结束后再继续执行;
- 主进程fork出子进程执行重写操作,保证主进程不会阻塞;
- 子进程遍历redis内存中数据到临时文件,客户端的写请求同时写入aof_buf缓冲区和aof_rewrite_buf重写缓冲区保证原AOF文件完整以及新AOF文件生成期间的新的数据修改动作不会丢失;
- ①子进程写完新的AOF文件后,向主进程发信号,父进程更新统计信息。
②主进程把 aof_rewrite_buf中的数据写入到新的AOF文件; - 使用新的 AOF文件覆盖旧的 AOF文件,完成 AOF重写。
3.6 AOF的优劣势
3.6.1 AOF的优势
- 备份机制更稳健,丢失数据概率更低;
- 可读的日志文本,通过操作AOF稳健,可以处理误操作。
3.6.2 AOF的劣势
- 比起RDB占用更多的磁盘空间;
- 恢复备份速度要慢;
- 每次读写都同步的话,有一定的性能压力;
- 存在个别Bug,造成恢复不能。
3.7 AOF小总结
- AOF文件是一个只进行追加的日志文件;
- Redis可以在AOF文件体积变得过大时,自动地在后台对AOF进行重写;
- AOF文件有序地保存了对数据库执行的所有写入操作,这些写入操作以Redis协议的格式保存,因此AOF文件的内容非常容易被人读懂,对文件进行分析也很轻松;
- 对于相同的数据集来说,AOF文件的体积通常要大于RDB文件的体积;
- 根据所使用的的 fsync策略,AOF的速度可能会慢于RDB。
四、RDB和AOF对比
- RDB持久化方式能够在指定的时间间隔能对你的数据进行快照存储;
- AOF持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以redis协议追加保存每次写的操作到文件末尾;
- Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大;
- 只做缓存:
如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化方式; - 同时开启两种持久化方式:
在这种情况下,当redis重启的时候会优先载入AOF文件来恢复原始的数据, 因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。 - RDB的数据不实时,同时使用两者时服务器重启也只会找AOF文件。那要不要只使用AOF呢?
建议不要,因为RDB更适合用于备份数据库(AOF在不断变化不好备份), 快速重启,而且不会有AOF可能潜在的bug,留着作为一个万一的手段。 - 性能建议
①因为RDB文件只用作后备用途,建议只在Slave上持久化RDB文件,而且只要15分钟备份一次就够了,只保留save 900 1这条规则;
②如果使用AOF,好处是在最恶劣情况下也只会丢失不超过两秒的数据,启动脚本较简单只 load自己的 AOF文件就可以了;
③代价,一是带来了持续的IO,二是AOF rewrite的最后将 rewrite过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的;
④只要硬盘许可,应该尽量减少 AOF rewrite的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上;
⑤默认超过原大小100%大小时重写可以改到适当的数值。