redis学习手记(二)持久化方式

it2023-02-14  81

为什么要将redis做持久化方式: 一般 redis主要的运用场景为作为后端的缓存层,redis中的数据都在内存,一旦服务器宕机,内存中的数据将全部丢失,所以有必要将数据做数据持久化。如果不做持久化,redis重新启动的话,每次都要去数据库读取数据然后再次把数据回写redis,容器造成数据压力过大。另一个原因就是每次去读取数据库,这种是比较缓慢的一个过程,性能远比不上从redis读取出来的快,导致运用程序的卡顿。故实现持久化是有必要,它避免从后端数据库中进行恢复。

redis持久化方式

AOF日志RDB快照

AOF日志方式 aof日志记录的方式:是先执行命令,把数据写入内存,然后才记录日志。这种就避免了执行命令有问题的时候也记录进去日志中不利于以后做日志数据还原的(只记录正确的执行命令)。

开启AOF日志功能 redis默认是不开启aof功能,所以要使用aof功能,需要去配置文件中把aof功能打开方式如下:

# 默认是为no,所以需要的时候将appendonly 置为yes appendonly yes # aof日志文件 appendfilename "appendonly.aof" # 具体的写入方式有以下三种方式,默认的为everysec(每秒写回:每个写命令执行完, # 只是先把日志写到 AOF 文件的内存缓冲区,每隔一秒把缓冲区中的内容写入磁盘;) # appendfsync always appendfsync everysec # appendfsync no

执行set keyTEST VALUE 在日志的记录表示为:*3”表示当前命令有三个部分,每部分都是由“$+数字”开头,后面紧跟着具体的命令、键或值。这里,“数字”表示这部分中的命令、键或值一共有多少字节。

*3 $3 set $7 keyTEST $5 VALUE

AOF存在的问题

如果刚执行完一个命令,还没有来得及记日志就宕机了,那么这个命令和相应的数据就有丢失的风险,还是会存在数据丢失的问题。AOF 虽然避免了对当前命令的阻塞,但可能会给下一个操作带来阻塞风险。(写入磁盘的速度远慢与内存对的操作)

AOF写回三种策略

Always,同步写回:每个写命令执行完,立马同步地将日志写回磁盘;Everysec,每秒写回:每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,每隔一秒把缓冲区中的内容写入磁盘;No,操作系统控制的写回:每个写命令执行完,只是先把日志写到 AOF 文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘。

三种写回策略都存在一定的问题,可以根据具体需求类选择不同的写回策略。 同步写回”可以做到基本不丢数据,但是它在每一个写命令后都有一个慢速的落盘操作,不可避免地会影响主线程性能。

操作系统控制的写”在写完缓冲区后,就可以继续执行后续的命令,但是落盘的时机已经不在 Redis 手中了,只要 AOF 记录没有写回磁盘,一旦宕机对应的数据就丢失了;

“每秒写回”采用一秒写回一次的频率,避免了“同步写回”的性能开销,虽然减少了对系统性能的影响,但是如果发生宕机,上一秒内未落盘的命令操作仍然会丢失。所以,这只能算是,在避免影响主线程性能和避免数据丢失两者间取了个折中。

AOF日志文件过大怎么办? 文件系统本身对文件大小有限制,无法保存过大的文件; 如果文件太大,之后再往里面追加命令记录的话,效率也会变低; 如果发生宕机,AOF 中记录的命令要一个个被重新执行,用于故障恢复,如果日志文件太大,整个恢复过程就会非常缓慢,这就会影响到 Redis 的正常使用。

解决aof文件过大的问题 aof日志文件过大,采用重写机制可以大大缓解这种问题,重写的本质就是“多变一”功能。所谓的“多变一”,也就是说,旧日志文件中的多条命令,在重写后的新日志中变成了一条命令对于相同的执行命令只记录最新的命令)。 重写过程是由后台线程 bgrewriteaof 来完成的,这也是为了避免阻塞主线程,导致数据库性能下降。

重写流程: 每次 AOF 重写时,Redis 会先执行一个内存拷贝,用于重写;然后,使用两个日志保证在重写过程中,新写入的数据不会丢失。而且,因为 Redis 采用额外的线程进行数据重写,所以,这个过程并不会阻塞主线程。(主线程 fork 出后台的 bgrewriteaof 子进程)

RDB快照方式 RDB是指:它实现类似照片记录效果的方式,就是把某一时刻的状态以文件的形式写到磁盘上,也就是快照。这样一来,即使宕机,快照文件也不会丢失,数据的可靠性也就得到了保证。这个快照文件就称为 RDB 文件,其中,RDB 就是 Redis DataBase 的缩写。 和 AOF 相比,RDB 记录的是某一时刻的数据,并不是操作,所以,在做数据恢复时,我们可以直接把 RDB 文件读入内存,很快地完成恢复

触发快照的操作: Redis 提供了两个命令来生成 RDB 文件,分别是 save 和 bgsave。save:在主线程中执行,会导致阻塞;bgsave:创建一个子进程,专门用于写入 RDB 文件,避免了主线程的阻塞,这也是 Redis RDB 文件生成的默认配置。 默认配置如:

save 900 1 save 300 10 save 60 10000

需要注意的是使用bgsave 避免阻塞。其中避免阻塞和正常处理写操作并不是一回事。此时,主线程的确没有阻塞,可以正常接收请求,但是,为了保证快照完整性,它只能处理读操作,因为不能修改正在执行快照的数据,所以修改操作的时候阻塞的。所以Redis 就会借助操作系统提供的写时复制技术(Copy-On-Write, COW),在执行快照的同时,正常处理写操作(类似于java的CopyOnWriteArrayList一样的思想)。bgsave 子进程是由主线程 fork 生成的,可以共享主线程的所有内存数据。bgsave 子进程运行后,开始读取主线程的内存数据,并把它们写入 RDB 文件(如果主线程对这些数据也都是读操作(例如图中的键值对 A),那么,主线程和 bgsave 子进程相互不影响。但是,如果主线程要修改一块数据(例如图中的键值对 C),那么,这块数据就会被复制一份,生成该数据的副本。然后,bgsave 子进程会把这个副本数据写入 RDB 文件,而在这个过程中,主线程仍然可以直接修改原来的数据。)

频繁的执行快照方式导致什么问题

频繁将全量数据写入磁盘,会给磁盘带来很大压力,多个快照竞争有限的磁盘带宽,前一个快照还没有做完,后一个又开始做了,容易造成恶性循环bgsave 子进程需要通过 fork 操作从主线程创建出来。虽然,子进程在创建后不会再阻塞主线程,但是,fork 这个创建过程本身会阻塞主线程,而且主线程的内存越大,阻塞时间越长。如果频繁 fork 出 bgsave 子进程,这就会频繁阻塞主线程了。

要解决频繁的快照可以使用增量快照,所谓增量快照,就是指,做了一次全量快照后,后续的快照只对修改的数据进行快照记录,这样可以避免每次全量快照的开销

数据不能丢失时,内存快照和 AOF 的混合使用使用两种的混合模式

混合模式: Redis 4.0 中提出了一个混合使用 AOF 日志和内存快照的方法。简单来说,内存快照以一定的频率执行,在两次快照之间,使用 AOF 日志记录这期间的所有命令操作。这样一来,快照不用很频繁地执行,这就避免了频繁 fork 对主线程的影响。而且,AOF 日志也只用记录两次快照间的操作,也就是说,不需要记录所有操作了,因此,就不会出现文件过大的情况了,也可以避免重写开销。

实现混合模式的原理为: fork出的子进程先将共享的内存副本全量的以RDB方式写入aof文件,然后在将重写缓冲区的增量命令以AOF方式写入到文件,写入完成后通知主进程更新统计信息,并将新的含有RDB格式和AOF格式的AOF文件替换旧的的AOF文件。简单的说:新的AOF文件前半段是RDB格式的全量数据后半段是AOF格式的增量数据。

总结:

数据不能丢失时,内存快照和 AOF 的混合使用是一个很好的选择;如果允许分钟级别的数据丢失,可以只使用 RDB;如果只用 AOF,优先使用 everysec 的配置选项,因为它在可靠性和性能之间取了一个平衡。
最新回复(0)