欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java学习笔记:Redis的持久化

程序员文章站 2022-07-05 10:59:12
...

fork()系统调用:

这里很突兀的来个fork()系统调用原因是应为:Redis的单线程的,那如果主线程去做这种耗时的IO同步操作时,Redis整体的性能会被拖垮的。

fork()它是一个系统调用,一般用它来创建一个和当前进程一模一样的子进程。当在程序中调用它时,系统为新的进程分配存储、资源,将原程序中的值也复制给他。

fork()函数调用一次会返回两次,在父进程得到的返回值是子进程的pid,在子进程中得到的是0,出错则返回负数。

Redis的实现是通过fork()系统调用创建一个子进程。 由这个子进程去负责执行这些耗时的IO操作,父子进程会共享内存,然后被共享的这块内存不可写,新的数据写入到新的内存文件中

RDB:

写RDB文件是Redis的一种持久化方式。在指定的时间间隔内将内存中的数据写入到磁盘,RDB文件是一个紧凑的二进制文件,每一个文件都代表了某一个时刻(执行fork的时刻)Redis完整的数据快照,恢复数据时,将快照文件读入内存即可。

RDB持久化的详细过程:

Redis会通过系统调用fork()出一个子进程,父子进程是会共享内存的,父进程和子进程共享的这块内存就是在执行fork操作那个时刻的内存快照。由linux的copy on write机制将父子进程共享的这块内存标记为只读状态。

此时对子进程来说,它的任务就是将这块只读内存中的数据保存成RDB文件。

对父进程来说它是有可能收到写命令的,当父进程尝试往这个加了只读状态的内存地址写入数据时,就会触发保护异常,执行linux的 copy on write,也就是将原来内存对应的数据页复制出来一份后,然后对这个副本进行修改。

这里就会出现一个丢数据的概念:你想,fork出来的子进程将要保存的数据是执行fork系统调用那个时刻的内存中的数据,很快这个内存就被标记为只读了,后续的增量数据没有写入到这个只读内存中,那就算是RDB成功生成了,然后好巧,Redis挂了,这些增量的数据就会丢(所以得使用AOF辅助)

第二种RDB出现数据的丢失的情况是:RDB过程中,直接失败了,文件都没生成,不光是增量数据,原来的数据都丢了。

RDB相关配置如下

# 把下面的注释打开就会禁用掉RDB的持久化策略
# save ""

# 快照相关,指的是在规定的时间内执行了多少次操作才会持久化到文件
save 900 1 # 900秒内1次
save 300 10 # 300秒内10次
save 60 10000 # 60秒内1万次

# 持久化出错了,是否让redis继续工作
stop-writes-on-bgsave-error yes

# 是否压缩RBD文件(redis会采用LZF压缩算法进行压缩)需要消耗CPU资源
rdbcompression yes

# 保存rbc文件时是否检验rbd文件格式
# 使用CRC64算法进行数据校验,但是这样会增加大约 10%的性能消耗
rdbchecksum yes

# dump DB的文件名
dbfilename dump.rdb

# rdb文件的持久化目录(当前目录)
dir ./

 触发保存RDB文件4种情况

  1. 手动执行save命令、bgsave
  2. 满足配置文件中配置的save相关配置项时,自动触发
  3. 手动执行flushall
  4. 关闭redisshutdown

如何让redis加载rdb文件?

只需要将rdb文件放在redis的启动目录下,redis其中时会自动加载它

RDB模式的优缺点:

优点:RDB过程中,由子进程代替主进程进行备份的IO操作。保证了主进程仍然提供高性能的服务。适合大规模的数据备份恢复过程。

缺点:

  1. 默认情况下,它是每隔一段时间进行一次数据备份,所以一旦出现最后一次持久化的数据丢失,将丢失大规模的数据。
  2. fork()子进程时会占用一定的内存空间,如果在fork()子进程的过程中,父进程夯住了,那也就是redis卡住了,不能对外提供服务。所以不要让生成RDB文件的时间间隔太长,不然每次生成的RDB文件过大对Redis本身也是有影响的。

AOF:

AOF是什么?

Append Only File,他也是Redis的持久化策略。即将所有的写命令都以日志的方式追加记录下来(只追加,不修改),恢复的时候将这个文件中的命令读出来回放。

当我们执行 flushall 命令,清空了redis在内存中的数据,appendonly.aof 同样会记录下这条命令,所以,我们想恢复数据的话,需要去除 appendonly.aof 里面的 flushall 命令

AOF相关的配置

# 默认不开启aof
appendonly no

# aof文件名
appendfilename "appendonly.aof"

# redis通过fsync()调用告诉操作系统实际在磁盘上写入数据
# aof文件落盘的策略
# appendfsync always 每次发生数据变更,立刻记录到磁盘,但是导致redis吞吐量降低
# appendfsync everysec 可能会丢失1秒的数据
# appendfsync no
appendfsync everysec

# 当时用bfwriteaof时,fork一个子进程写aof文件,就算aof文件很大,也不会阻塞主进程
# 意外情况:但是当主进程、子进程同时写aof文件时,可能会出现由于子进程大量的IO操作阻塞主进程
# 当出现这种意外情况时:设置这个参数为no,可以保证数据不会丢失,但是得容忍主进程被阻塞
# 当出现这种意外情况时:设置这个参数为yes,主进程不会被阻塞主,但是不保证数据安全性
# 综上:如果应用无法忍受延迟:设置为yes。无法忍受数据丢失:设置为no
no-appendfsync-on-rewrite no

# 在当前aof文件的体积超过上次aof文件的体积的100%时,写新文件
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb # 最开始的aof文件体积至少达到60M时才重写

# 回放aof文件时,如果最后一条命令存在问题,是否允许忽略
aof-load-truncated yes

# 是否允许AOF和RDB这两种持久化方式并存
aof-use-rdb-preamble yes

当aof文件出错怎么办?

redis为我们提供了修复aof文件的工具

Java学习笔记:Redis的持久化

[[email protected] bin]# redis-check-aof  --fix appendonly.aof

 

aof模式的优缺点 优点:

  1. aof是用追加的形式写,没有随机磁盘IO那样的寻址开销,性能还是比较高的。
  2. aof可以更好的保护数据不丢失或者尽可能的少丢失:设置让redis每秒同步一次数据,即使redis宕机了,最多也就丢失1秒的数据。
  3. 即使aof真的体积很大,也可以设置后台重写,不影响客户端的重写。
  4. aof适合做灾难性的误删除紧急恢复:比如不小心执行了flushall,然后可以在发生rewrite之前 快速备份下aof文件,去掉末尾的 flushall,通过恢复机制恢复数据

缺点:使用aof一直追加写,导致aof的体积远大于RDB文件的体积,恢复数据、修复的速度要比rdb慢很多。

aof的重写

AOF采取的是文件追加的方式,文件的体积越来越大,为了优化这种现象,增加了重写机制,当aof文件的体积到达我们在上面的配置文件上的阕值时,就会触发重写策略,只保留和数据恢复相关的命令

手动触发重写

# redis会fork出一条新的进程
# 同样是先复制到一份新的临时文件,最后再rename,遍历每一条语句,记录下有set的语句
bgrewriteaof

RDB和AOF的选择:

  • 如果我们的redis只是简单的作为缓存,那两者都不要也没事
  • 如果数据需要持久化,那不要仅仅使用RDB,因为一旦发生故障,你会丢失很多数据
  • 同时开启两者: 在这种情况下,redis优先加载的是aof,因为它的数据很可能比rdb更全,但是并不建议只是用aof,因为aof不是那么的安全,很可能存在潜在的bug

推荐:

  • 建议在从机slave上只备份rdb文件,而且只要15分钟备份一次就够了。
  • 如果启动了aof,我们尽量减少rewrite的频率,基础大小设置为5G完全可以,起步也要3G。
  • 如果我们不选择aof, 而是选择了主从复制的架构实现高可用同样可以,能省掉一大笔IO操作,但是意外发生的话,会丢失十几分钟的数据。