聊一聊Redis是如何做到“持久化”的
Posted 守夜人爱吃兔子
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了聊一聊Redis是如何做到“持久化”的相关的知识,希望对你有一定的参考价值。
什么是持久化?
对于持久化某度某科的解释是:
持久化是将程序数据在持久状态和瞬时状态间转换的机制。通俗的讲,就是瞬时数据(比如内存中的数据,是不能永久保存的)持久化为持久数据(比如持久化至数据库中,能够长久保存)
所以对于持久化狭义的理解就是: “持久化”仅仅指把域对象永久保存到数据库中;可以广义的理解为:“持久化”包括和数据库相关的各种操作(持久化就是将有用的数据以某种技术保存起来,将来可以再次取出来应用,数据库技术,将内存数据一文件的形式保存在永久介质中(磁盘等)都是持久化的例子)。
数据持久化对象的基本操作有:保存、更新、删除、加载、查询等。
保存:把域对象永久保存到数据库。
更新:更新数据库中域对象的状态。
删除:从数据库中删除一个域对象。
加载:根据特定的OID,把一个域对象从数据库加载到内存。
查询:根据特定的查询条件,把符合查询条件的一个或多个域对象从数据库加载内在存中。
为什么需要持久化
通过前面的介绍我们知道Redis的数据全部在内存里,所以万一出现突然出现故障发生宕机,那么保存在内存中的数据就会全部丢失,因此必须有一种机制来保证Redis的数据不会因为故障而丢失,这种机制就是Redis的持久化机制。
除此之外使用数据持久化还有以下好处:
1、持久化技术封装了数据访问细节,为大部分业务逻辑提供面向对象的API。
2、程序代码重用性强,即使更换数据库,只需要更改配置文件,不必重写程序代码。
3、业务逻辑代码可读性强,在代码中不会有大量的SQL语言,提高程序的可读性。
4、持久化技术可以自动优化,以减少对数据库的访问量,提高程序运行效率。
5、松散耦合,使持久化不依赖于底层数据库和上层业务逻辑实现,更换数据库时只需修改配置文件而不用修改代码。
如何持久化
对于数据的持久化主要有以下的五个过程:
1、客户端向服务端发送写操作(数据在客户端的内存中)。
2、数据库服务端接收到写请求的数据(数据在服务端的内存中)。
3、服务端调用write这个系统调用,将数据往磁盘上写(数据在系统内存的缓冲区中)。
4、操作系统将缓冲区中的数据转移到磁盘控制器上(数据在磁盘缓存中)。
5、磁盘控制器将数据写到磁盘的物理介质中(数据真正落到磁盘上)。
这5个过程是在理想条件下一个正常的保存流程,但是在大多数情况下,我们的机器等等都会有各种各样的故障,这里划分了两种情况:
(1)Redis数据库发生故障,只要在上面的第三步执行完毕,那么就可以持久化保存,剩下的两步由操作系统替我们完成。
(2)操作系统发生故障,必须上面5步都完成才可以。
在这里只考虑了保存的过程可能发生的故障,其实保存的数据也有可能发生损坏,需要一定的恢复机制,篇幅有限这里不进行展开。
Redis对于数据的持久化同样需要通过以上五个步骤,在Redis4.x中对于数据的持久化方案主要有三种策略机制:
1、RDB(Redis DataBase)
2、AOF(Append Only File)
3、混合型方案
RDB机制
RDB机制是Redis里面默认提供的一套持久化技术方案,专门用于保证内存中的数据被写入到磁盘里面去,是指在指定的时间间隔内将内存中的数据集快照写入磁盘。快照是数据存储的某一时刻的状态记录,可以理解为把当前时刻的数据保存下来,由于是某一时刻的快照,那么快照中的值要早于或者等于内存中的值。这种方式是通过修改相应的redis.conf来实现的,默认的文件名为dump.rdb。在我们安装了redis之后,所有的配置都是在redis.conf文件中,里面保存了RDB和AOF两种持久化机制的各种配置。
比如:
redis.config中的语句主要是用于将数据持久化在redis的dump.rdb文件里面,然后每一次开启redis的时候都会将里面的数据重新加载,进行数据恢复。dump.rdb的存放位置由下边的dir来标识。
前面说了RDB机制是通过把某个时刻的所有数据生成一个快照来保存,既然是快照那么就应该有一种触发机制,来触发快照的功能。对于RDB来说,提供了三种机制来触发快照,分别是:save、bgsave、自动触发机制。
rdb里面并不是单纯将数据直接存储起来,而是通过一种特定的lzf压缩方式来实现的:
这个配置开关最好打开,默认是开启的,否则会导致rdb文件过大,占用磁盘空间。
触发rdb存储机制的条件有两种:
1、手动触发:
1、执行save指令:这是一种阻塞式指令,阻塞当前redis服务器,直RDB过程完成为止。如果内存比较大会造成redis长时间阻塞,所以线上禁止使用。具体流程可以总结如下:
2、执行bgsave命令:执行该命令时,Redis会在后台异步进行快照操作,快照同时还可以响应客户端请求。具体操作是redis进程执行fork操作创建子进程,RDB持久化过程由子进程负责,完成后自动结束。阻塞只发生在fork阶段,时间一般很短,具体和实例数据大小有关系。具体流程可以总结如下:
2、自动触发:
除了执行上面的两种命令手动触发之外,redis还存在以下4种方式自动触发RDB的持久化机制。
1、使用save相关配置,如“save m n” 表示m秒内数据集存在n次修改时,自动触发bgsave。也可以同时配置多个条件,只要其中一个达标就触发。
config文件中save机制的配置如下:
save <指定时间间隔> <执行指定次数更新操作> (默认是save 900 1)
可以理解为指定的秒内如果进行了指定次数的更新操作,就会将数据存储到dump.rdb文件中去。
2、主从复制时,从节点执行全量复制操作,主节点自动执行bgsave生成RDB文件并发送给从节点;
3、执行debug reload命令时重新加载redis
4、默认情况下执行shutdown命令时,如果没有开启aof持久化则自动执行bgsave
每项技术都会有一定的优点和缺点,RDB的优缺点可以总结如下:
优点:
1、RDB适合大规模的数据恢复。
2、RDB是一个紧凑压缩的二进制文件,占用的内存比较小,代表数据在某一个时间点的快照。非常适合备份,全景复制。比如每三个小时执行bgsave备份,并把RDB文件上传到备份服务器中,用于防止Redis服务器不可恢复性问题。
3、RDB恢复数据速度快于AOF的方式。
4、RDB 可以最大化 redis 的性能。父进程在保存 RDB 文件时唯一要做的就是 fork 出一个子进程,然后这个子进程就会处理接下来的所有保存工作,父进程无须执行任何磁盘 I/O 操作。
5、RDB 非常适用于灾难恢复(disaster recovery),因为它只有一个文件,并且内容都非常紧凑,可以(在加密后)将它传送到别的数据中心。
缺点:
1、RDB 在服务器故障时容易造成数据的丢失。RDB允许通过修改配置来控制持久化的频率。但是,因为RDB文件需要保存整个数据集的状态,所以它是一个比较繁重的操作,如果频率太频繁,会对Redis性能产生一定的影响。所以一般可以设置至少5分钟才保存一次快照,这时如果 Redis 出现宕机等情况,也就意味着最多可能丢失5分钟数据。所以数据的完整性和一致性不高。
2、备份时会占用内存。因为Redis 在备份时会独立创建一个子进程并且采用的是 copy-on-write 的方式,在 Redis 执行 RDB 持久化期间,如果 client 写入数据很频繁,那么将增加 Redis 占用的内存,最坏情况下,内存的占用将达到原先的2倍。刚 fork 时,主进程和子进程共享内存,但是随着主进程需要处理写操作,主进程需要将修改的页面拷贝一份出来,然后进行修改。极端情况下,如果所有的页面都被修改,则此时的内存占用是原先的2倍。
3、如果数据比较大时,备份的时候会比较耗时RDB 保存时使用 fork 子进程进行数据的持久化时,如果数据比较大的话,fork 可能会非常耗时,造成 Redis 停止处理服务N毫秒。如果数据集很大且 CPU 比较繁忙的时候,停止服务的时间甚至会到一秒。
4、同上因为bgsave每次运行都要执行fork操作创建子进程,属于重量级操作,频繁执行成本过高,所以RDB方式数据没办法做到实时持久化/秒级持久化。
5、因为RDB文件使用特定二进制格式保存,Redis版本演进过程中有多个格式的RDB版本,所以存在老版本Redis服务无法兼容新版RDB格式的问题。
AOF机制
前面说了虽然RDB能够作为redis的持久化进行数据的备份,但是也存在一些不足。所以为了弥补RDB的缺点,aof机制应势而出。aof的全称是append only file,它是以独立日志的形式来将每一次写操作都进行记录,追加到相应的文件中去。Redis重启的时候会根据日志文件的内容从前到后执行一次达到数据恢复的目的。前面说到RDB有个缺点就是数据备份的实时性比较低,所以AOF的主要作用是解决了数据持久化的实时性,目前AOF已经是Redis持久化的主流。
在conf文件中同样有对于aof的配置:
appendonly:默认值为no,因为r前面说过Redis 默认使用的是rdbd的持久化方式,如果想要开启 AOF 持久化方式,需要将 appendonly 修改为 yes。
appendfilename :指的是aof文件名,默认是"appendonly.aof"
appendfsync:aof持久化策略的配置,可取的值有三种:always、everysec和no。
设置为always时,会极大消弱Redis的性能,因为这种模式下每次write后都会调用fsync立马将数据写入磁盘进行持久化(Linux为调用fdatasync)。虽然性能差,但是安全性是最高的。
设置为no时,则write后不会有fsync调用,由操作系统自动调度刷磁盘,直接写入aof的内存缓冲区,性能是最好的。
设置为everysec时,最多每秒调用一次fsync,这种模式性能并不是很糟糕,一般也不会产生毛刺,这归功于Redis引入了BIO线程,所有fsync操作都异步交给了BIO线程。这也是推荐的策略模式,就如同配置说明上说的:If unsure, use "everysec"(如果不确定就是使用everysec)。
它们三个可以总结为:
策略 | 写回时机 | 优点 | 缺点 |
Always | 同步写回 | 数据基本不会丢失,可靠性好 | 每个命令都要写入磁盘,性能较差 |
No | 系统控制 | 性能好 | 安全性差,宕机时丢失数据较多 |
Everysec | 每秒写回 | 性能中等 | 宕机时丢失一秒内的数据 |
AOF里触发该机制的条件可以在conf文件里面进行配置:
当AOF文件的大小超过所设定的阈值时,Redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集。
配置参数:
auto-aof-rewrite-min-size 64mb
表示aof文件要达到一定体积才会进行重写
auto-aof-rewrite-percentage 100
表示当aof文件大小增长了100%才会触发重写
重写的原理
aof在进行重写处理的时候,会额外开辟一个bgrewriteaof线程,将原先的aof文件数据拷贝到一个临时文件进行处理,最后再覆盖原先文件。AOF文件重写过程与RDB快照bgsave工作过程有点相似,都是通过fork子进程,由子进程完成相应的操作,同样的在fork子进程简短的时间内,redis是阻塞的。具体过程如下图所示:
相比于rdb而言,aof的数据备份效率更高,但是在数据恢复的时候其性能却远远不如rdb高效。
混合型策略
重启 Redis 时,如果采用rdb来恢复内存状态,会丢失大量数据。
如果使用 aof,性能相对rdb来说要慢很多,这样在Redis数据很大的情况下,启动的时候时间消耗比较大。
混合型策略是Redis4.0开始添加的新的混合型持久化方案。混合型方案就是将前面的RDB持久化方案以及AOF持久化方案结合。
混合持久化同样也是通过bgrewriteaof完成的,不同的是当开启混合持久化时,fork出的子进程先将共享的内存副本全量的以RDB方式写入aof文件,然后在将aof_rewrite_buf重写缓冲区的增量命令以AOF方式写入到文件,写入完成后通知主进程更新统计信息,并将新的含有RDB格式和AOF格式的AOF文件替换旧的的AOF文件。可以简单的理解为:新的AOF文件前半段是RDB格式的全量数据后半段是AOF格式的增量数据,可以理解为:
这样做的好处是可以结合 rdb 和 aof 的优点, 快速加载同时避免丢失过多的数据,而缺点是 aof 里面的 rdb 部分就是压缩格式不再是 aof 格式,可读性差。
混合持久化默认关闭的,可以通过aof-use-rdb-preamble配置参数控制,yes则表示开启,no表示禁用,默认是禁用的。
aof-use-rdb-preamble yes # yes:开启,no:关闭
如果开启了混合策略,那么在启动redis时依然优先加载aof文件进行数据恢复,而aof文件加载可能有两种情况如下:
1、如果aof文件开头是rdb的格式, 先加载 rdb内容再加载剩余的 aof。
2、如果aof文件开头不是rdb的格式,直接以aof格式加载整个文件。
虽然混合策略是rdb和aof结合的,但是它有优点同样也还有一定的缺点:
优点:
混合持久化结合了RDB持久化 和 AOF 持久化的优点, 由于绝大部分都是RDB格式,加载速度快,同时结合AOF,增量的数据以AOF方式保存了,数据更少的丢失。
缺点:
兼容性差,一旦开启了混合持久化,在4.0之前版本都不识别该aof文件。
同时由于前部分是RDB格式,阅读性较差。
总结
以上主要介绍了redis对数据进行持久化的三种策略,分别是:RDB、AOF以及基于其两种方案的混合型持久策略。这三种策略都有自己的优点和缺点,实际使用中可以根据实际的场景和需求进行合理的选择。
最后
最近我整理了整套《JAVA核心知识点总结》,说实话 ,作为一名Java程序员,不论你需不需要面试都应该好好看下这份资料。拿到手总是不亏的~我的不少粉丝也因此拿到腾讯字节快手等公司的Offer
进【Java进阶之路群】,找管理员获取哦-!
以上是关于聊一聊Redis是如何做到“持久化”的的主要内容,如果未能解决你的问题,请参考以下文章