Redis原理之持久化

发布于:2025-07-16 ⋅ 阅读:(19) ⋅ 点赞:(0)

上篇文章:

Redis客户端使用(Client、Java、SpringBoot)https://blog.csdn.net/sniper_fandc/article/details/149140091?fromshare=blogdetail&sharetype=blogdetail&sharerId=149140091&sharerefer=PC&sharesource=sniper_fandc&sharefrom=from_link

目录

1 持久化策略

2 RDB

2.1 手动触发

2.2 自动触发

2.2.1 配置文件配置

2.2.2 shutdown命令

2.2.3 主从复制

2.3 RDB优缺点

3 AOF

3.1 使用AOF

3.2 AOF写入流程

3.3 AOF重写(rewrite)流程

3.3.1 手动触发

3.3.2 自动触发


1 持久化策略

        Redis为了防止因为进程退出导致的数据丢失,会把数据持久化到硬盘作为备份,下次重启时就可以从硬盘读取恢复数据。常见的备份策略有两个:定期备份和实时备份,对应的Redis持久化的策略也有两个:RDB(Redis DataBase)和AOF(Append Only File),RDB对应定期备份,AOF对应实时备份。

2 RDB

        RDB就是定期把Redis内存中的数据生成一个“快照”(某一时刻的数据状态),写入硬盘中。Redis在启动时,会读取RDB文件,并把数据恢复到内存中。如果RDB文件损坏,就可能导致Redis启动失败,也可能Redis会启动成功,但是数据是异常的。

        定期到底是什么时候,具体来讲有两种触发方式:

2.1 手动触发

        用户手动调用命令来触发快照生成,命令有save和bgsave两个。save命令执行时,Redis不会执行其他命令,而是只进行快照生成和持久化(这里没有文件替换的过程,直接是打开RDB文件进行写入)。因此save命令可能会阻塞Redis服务器,不建议使用。

        bgsave命令(bg表示background)使用多进程来实现并发编程模型,执行流程如下:

        注意:并发编程模型不一定是由多线程实现,也可以使用多进程实现。

        1.父进程接收到bgsave命令后,首先判断是否有其他子进程正在运行,比如已经有子进程正在执行RDB文件的生成。如果有,就命令返回。

        2.如果没有,父进程执行fork系统调用(Linux提供的创建子进程的API),创建一个子进程。

        注意:fork创建子进程是直接把父进程复制一份作为子进程。这里复制的内容主要有进程PCB、虚拟地址空间(内存中的数据)、文件描述符表等等,由于父进程和子进程中内存中的数据一模一样,且打开的文件描述符表也一样,因此子进程执行持久化的效果和父进程执行持久化的效果就一样。

        并且fork在执行复制操作时,采用“写时拷贝”机制,即不是把所有数据都拷贝,而是先共用一份内存地址空间(即共享同一份数据),当某一方对某些数据进行修改时,才会触发对该数据的真正的拷贝。短时间内,父进程不会有大量数据修改,因此如果内存数据很多,使用fork的效率也会比较高。

        3.父进程创建子进程后,继续响应其他命令,进而也不会阻塞Redis服务器。

        4.子进程负责RDB文件的生成,直到持久化的完成。

        5.子进程持久化完成后,发送信号给父进程通知,父进程更新统计信息,然后子进程就可以结束销毁。

关于步骤4,有一些细节需要关注:

        保存路径:RDB文件的默认存储在Redis的工作目录下(可以通过配置文件配置路径,在dir属性名后配置路径)。RDB生成的镜像文件默认采用压缩的方式存储(方便网络传输和存储),是二进制文件,默认名dump.rdb。

        保存流程:先把要持久化的数据保存到临时文件中,待所有数据保存完成后,删除旧的dump.rdb快照,把临时文件名修改为dump.rdb,保证快照文件始终只有一份。

        校验:Redis默认开启了RDB机制,如果Redis启动时加载到损坏的RDB文件会拒绝启动。可以使用Redis提供的redis-check-rdb*工具检测RDB文件并获取对应的错误报告。

2.2 自动触发

        自动触发有3种情况:

2.2.1 配置文件配置

        在Redis的配置文件中,可以配置让Redis每隔多长时间或每修改多少次就触发持久化。

        具体在配置文件的:save <seconds> <changes>,seconds表示秒级时间,changes表示修改次数,即在seconds时间内,如果修改次数超过changes,seconds时间过后就自动触发RDB文件的生成。不过这也可能带来RDB文件和Redis内存中数据不一致的情况,比如在下一次快照的持久化时间前,Redis进行了大量key的修改,但是此时没有过seconds的时间,恰好Redis服务器崩溃,再次重启Redis服务器时读取的还是旧的快照,即这期间的key的修改的数据丢失了。。

        注意:虽然可以灵活配置,但是生成RDB文件的成本还是比较高,因此不能让自动触发的操作太频繁。可以配置save “”来关闭自动触发。

2.2.2 shutdown命令

        通过shutdown命令主动正常关闭Redis服务器,Redis服务器也会自动触发RDB文件的生成。如果在Linux中使用service redis-server restart命令(重启redist服务器),背后也是调用shutdown命令,属于正常关闭的方式,因此也会自动触发持久化。

        注意:如果异常关闭Redis服务器,就不会自动触发。比如使用kill命令杀死Redis进程,或者服务器崩溃,或者服务器所在主机重启。这种情况下,就有可能导致数据丢失。

2.2.3 主从复制

        Redis进行主从复制时,主节点会自动生成RDB快照文件,然后通过网络传输发送给从节点。

2.3 RDB优缺点

        1.RDB是个压缩的进制件,适于备份全量复制等场景。

        2.Redis加载RDB恢复数据远远快于AOF(二进制文件,体积小,读写速度快)

        3.RDB式数据没办法做到实时持久化。因为bgsave每次运都要执fork创建进程,属于重量级操作,频繁执成本过

        4.RDB件使特定进制格式保存,Redis版本演进过程中有多个RDB版本,兼容性可能有险。

3 AOF

        AOF是实时的持久化机制,但是不是把数据持久化到硬盘中,而是把操作命令持久化到独立日志文件中,每次Redis重启时会读取该文件重新执行操作命令,从而实现数据恢复。

3.1 使用AOF

        需要在Redis的配置文件中配置appendonly,no表示不开启(默认),yes表示开启。开启后,数据恢复加载的就是AOF文件。

        appendfilename配置用于配置AOF文件名,默认是appendonly.aof。aof文件是文本文件,而不是二进制文件。

        持久化工作目录也是dir配置。

        注意:引入AOF不意味着对性能影响更大了(AOF要读写磁盘)。原因是因为1.AOF不是每次有一个操作需要写入就立即写入到磁盘的,而是先写入缓冲区,缓冲区累积一定数量的操作后再一次性写入磁盘(此时如果发生Redis异常终止,就会导致数据丢失),降低了磁盘IO次数。2.由于AOF是对一个文件进行续写,从磁盘角度来看是顺序写入(顺序访问),比随机访问的速度快。因此AOF实际对性能没有太大影响。

3.2 AOF写入流程

        1.所有修改操作追加到AOF缓冲区中。

        2.AOF缓冲区根据刷新策略,定期向磁盘中同步缓存的操作。

        3.AOF越来越大,就定期触发rewrite重写,对aof文件进行压缩。

        4.当启用AOF,Redis重启时,会把aof文件的操作重新执行一遍,数据也就载入Redis内存中。

        对于步骤2,刷新策略有3种,均可在配置文件配置appendfsync的值:

配置项

含义

always

操作写入缓冲区后立即调用fsync(系统调用)同步到磁盘中

everysec

操作写入缓冲区后不调用fsync,只进行write(也是系统调用),每秒由同步线程进行fsync

no

操作写入缓冲区后只进行write,由操作系统控制fsync

        默认配置是everysec,频率适中,兼顾性能和数据安全。always虽然没有数据丢失,但是频率太高,性能低下。no虽然性能很高,但是数据安全无法保证。

        注意:系统调用fsync和write的区别。write是延迟写,即会把数据写入Linux提供的页缓冲区,由Linux控制同步硬盘的系统调用机制,因此也会出现数据丢失问题。而fsync是强制写硬盘,会阻塞直到写入完成。

3.3 AOF重写(rewrite)流程

        为什么要重写?答:让AOF文件占用体积变小,从而减少Redis启动时间。

        为什么会体积变小呢?答:AOF文件的目的是可以通过操作恢复数据,只保留能恢复数据的操作就好了。但是文件可能存在冗余操作,比如多次修改同一个key的值,那么只保留最后一次修改操作就好了。重写可以删除(或合并)一些冗余操作,从而让文件更小。

        AOF重写也有手动触发和自动触发两个时机:

3.3.1 手动触发

        调用bgrewriteaof命令。

3.3.2 自动触发

        配置文件配置auto-aof-rewrite-min-size和auto-aof-rewrite-percentage,auto-aof-rewrite-min-size表示触发重写时AOF文件的最小体积,默认64MB。auto-aof-rewrite-percentage表示触发重写时AOF文件大小相当于上次重写后文件增加的比例。

        1.当执行AOF重写时,如果当前进程正在执行AOF重写,请求不执行。如果当前进程正在执行bgsave操作,重写命令延迟到bgsave完成之后再执行。

        2.父进程调用fork创建子进程。

        3.父进程调用fork后,如果有新的命令操作请求到来,就继续执行,并把操作命令写入到aof_buf缓冲区,同时也会写到aof_rewrite_buf缓冲区。这里aof_buf是旧AOF文件的缓冲区,而aof_rewrite_buf是子进程没有的数据的新AOF文件的缓冲区。

        4.子进程在创建后执行重写,由于子进程已经有父进程fork前的数据了,而AOF文件最终目的是恢复数据,因此只需要把内存中的数据重写到新的AOF文件中。即新的AOF文件中的内容目前是文本类型的数据,而不是命令操作(重写命令操作还得在恢复数据时候重新执行命令操作,速度更慢了)。

        注意:子进程的重写类似于RDB快照文件的生成,因此Redis为了速度,引入混合持久化(配置文件中配置项aof_use_rdb_preamble可以修改是否开启),即开启混合持久化后子进程重写新的AOF文件存储的是和RDB一样的二进制数据,而在后面aof_rewrite_buf缓冲区追加操作新的AOF文件存储的是文本形式的命令操作。

        由于内存中的数据已经是所有最终操作命令后的数据,即没有冗余操作,因此写入内存中的数据就已经天然完成了删除和合并命令的操作。

        5.子进程重写结束后,发送信号通知父进程。

        6.父进程把aof_rewrite_buf缓冲区新增的命令操作追加到新的AOF文件中。

        7.用新的AOF文件代替旧的AOF文件。

        注意:在步骤3中,既然新的AOF文件最终会代替旧的AOF文件,为什么父进程还要根据原有的AOF写入流程把新来的命令操作写入旧的AOF文件?这其实是出于容错的考虑,因为如果在重写过程中,Redis发生异常崩溃,那么子进程的数据就会丢失,新的AOF文件还不完整,此时旧的AOF文件一直在正常写入,从而保证了极端情况下的数据安全。

下篇文章:


网站公告

今日签到

点亮在社区的每一天
去签到