当前位置:网站首页 > 办公教程 > 正文

一文带你搞懂Redis持久化

访客 发布于2023-10-02 办公教程 101

Redis持久化

Redis的数据是存储在内存的,当程序崩溃或者服务器宕机,那么内存里的数据就会丢失。所以避免数据丢失的情况,需要将数据保存到其他的存储设备中。

Redis提供两种方式来持久化,分别是

  • RDB(Redis Database):记录某个时刻的全部数据,本质是存储二进制数据的快照到磁盘,后续通过加载RDB文件恢复数据

  • AOF(Append Only File):记录执行的每条命令,通过重新执行命令来恢复数据。本质是记录操作日志,后续通过日志恢复数据。

RDB

本质

记录某个时刻的全部数据,本质是:将某一时刻Redis服务器内存中的数据保存到磁盘中的二进制文件中(dump.rdb),后续通过加载RDB文件恢复数据。

RDB触发(执行持久化)

RDB的触发包括手动触发,自动触发,关闭时持久化

  1. 手动触发包括主动执行sava命令,主动执行bgsave命令:

  • save命令:会在redis主线程执行持久化操作,会阻塞主线程,只有RDB持久化完成,才能响应客户端的请求,所以在生产模式的时候慎用(不用)。Redis在正常关闭时,会执行save命令来持久化数据

  • bgsave命令:background save,即后台保存。Redis会创建一个子进程来生成快照文件,创建子进程可能会有短暂的阻塞,而父进程(Redis的服务器进程)会继续处理客户端的请求。一般用的是这个命令

  1. 自动触发:

用户可以在Redis的配置文件中进行设置(默认开启),Redis会根据设置自动调用bgsave。比如,

#save <seconds> <changes>
save 900 1 表示每900秒内,有1条写数据操作,则触发bgsave
save 300 10 多条命令是并集关系,如果满足其中一个就会触发
RDB执行流程

Redis官网描述:

How it works
Whenever Redis needs to dump the dataset to disk, this is what happens:
1.Redis forks. We now have a child and a parent process.
2.The child starts to write the dataset to a temporary RDB file.
3.When the child is done writing the new RDB file, it replaces the old one.
This method allows Redis to benefit from copy-on-write semantics.
  1. Redis fork出一个子进程

  2. 子进程把数据写入临时的RDB文件

  3. 写完之,新RDB文件替换旧RDB文件

最后还有一句话:这种方式让Redis从”写时复制“技术收益。也就是,Redis在执行持久化的时候,依然可以处理客户端的命令,数据是可以被修改的。

具体来讲,fork创建子进程后,子进程会复制父进程的页表,页表指向的物理地址是同一个,所以子进程和父进程时共享同一片数据的。当父进程处理客户端请求,发生内存数据修改时,物理内存才会被复制一份给子进程,所以子进程的持久化操作是不会被影响。

AOF

记录执行的每条命令,通过执行命令来恢复数据。本质是记录操作日志,后续通过日志恢复数据。

AOF日志文件其实就是普通的文本,可以通过cat命令查看里面的内容。

Redis先执行写操作命令后,再将该命令记录到AOF日志中。有以下几点好处:

  1. 避免额外的检查开销。如果命令的语法有问题又不进行检查,直接先记录到AOF日志里,等到Redis使用日志文件恢复数据的时候,就会出错。所以先执行命令后,只有命令执行成功才记录到AOF日志中,就能避免额外的检查开销,保证AOF日志里的命令是正确的。

  2. 不会阻塞当前操作命令的执行。

当然也存在一定风险:

  1. 执行写操作和记录AOF日志是两个过程,当Redis还没有来得及将命令写入硬盘时,服务器宕机,数据就有丢失的风险

  2. 会阻塞下一个操作命令的执行。

因为执行命令和记录AOF日志都是在主进程完成的

开启AOF

打开redis配置文件

appendonly no
​
# The name of the append only file (default: "appendonly.aof")
​
appendfilename "appendonly.aof"

默认AOF是关闭的,通过设置appendonly为yes可开启

AOF写入流程
  1. 将数据写入AOF缓冲区中,这个缓冲区叫aof_buf,是一个sds数据

  2. 将aof_buf的数据写入AOF文件,此时还没有写入硬盘,而是拷贝到内核缓冲区page cache中。一共有4个时机,会调用一个flushAppendOnlyFile的函数,这个函数会使用write函数来将数据写入操作系统缓冲区

    1. 处理完事件后,等待下一次事件之前,也就是beforeSleep中

    2. 周期函数serverCron中

    3. 服务器退出之前的准备工作时

    4. 通过配置指令关闭AOF功能时

    内核缓冲区page cache:是操作系统内核中用于缓存磁盘数据的一部分内存区域

    硬盘缓冲区:是硬盘或磁盘控制器中的硬件缓存

  3. 将内核缓冲区的数据写入磁盘。即调用系统的flush函数,刷盘其实还是在flushAppendOnlyFile函数中,但是不一定调用了flushApeendOnlyFile,flush就一定会被调用。这里支持了刷盘时机的配置。

    /* Perform the fsync if needed. */
    if (server.aof_fsync == AOF_FSYNC_ALWAYS) {
        /* redis_fsync is defined as fdatasync() for Linux in order to avoid
         * flushing metadata. */
        latencyStartMonitor(latency);
        redis_fsync(server.aof_fd); /* Let's try to get this data on the disk */
        latencyEndMonitor(latency);
        latencyAddSampleIfNeeded("aof-fsync-always",latency);
        server.aof_last_fsync = server.unixtime;
    } else if ((server.aof_fsync == AOF_FSYNC_EVERYSEC &&
                server.unixtime > server.aof_last_fsync)) {
        if (!sync_in_progress) aof_background_fsync(server.aof_fd);
        server.aof_last_fsync = server.unixtime;
    }

注意:

  1. 为什么先将数据写入aof缓冲中,而不直接同步到磁盘呢?

    因为实时写入磁盘会带来很高的磁盘IO,影响性能

AOF写入策略(AOF日志写入磁盘时机)

Redis提供了三种写入策略,决定了AOF日志什么时候写入磁盘,包括

  • appendfsync always:每次写操作命令执行完之后,同步将AOF日志数据写入磁盘。

  • appendfsync everysec:每次写操作命令执行完之后,先将命令写入到AOF文件的内核缓冲区,然后每隔一秒将缓冲区里的内容写入磁盘

  • appendfsync no:Redis不去控制写入硬盘的时机,由操作系统控制。每次先将命令写入AOF文件的内核缓冲区,再由操作系统决定何时写入硬盘。一般情况linux会每30秒刷一次盘。这种策略对性能的影响最小,但发生崩溃时会丢失较多数据。

Redis的默认设置是每秒钟同步一次数据到磁盘,提供较好的性能和数据可靠性。也需要根据实际业务来选择,比如只是简单的缓存,不存在热点缓存,就可以30秒同步一次数据到磁盘中。Always策略每次执行写操作就同步将AOF日志写入硬盘,影响主进程的性能。

根据业务场景进行选择:

  • 如果要高性能,就选No策略

  • 如果要高可靠,就选Always策略

  • 如果折中,较好的性能和可靠性,就选Everysec策略

AOF重写

随着命令越来越多,AOF文件会越来越大,不仅占用大量磁盘空间,而且恢复数据也会变慢。

在AOF文件中,有些命令是没有作用的,比如一开始set一个数据为10,后来又set为100,那么前面set为10就没有作用了,完全可以把前面set为10的命令去掉。

所以,AOF重写机制的妙处在于,尽管某个键值对被多条命令重复修改,最终也只需用一条命令去记录这个键值对当前的最新状态,进而减少AOF日志中的命令数量。

AOF重写流程

写入AOF日志的操作是在主进程完成的,因为它写入的内容不多,所以不太影响命令的操作。

而AOF重写时,需要读取所有缓存的键值对数据,为每一个键值对生成一条命令,然后写入到新的AOF文件,重写完后,将旧AOF文件替换掉。所以AOF过程由后台子进程来完成,避免阻塞主进程。

这里使用子进程而不是线程,因为如果是使用线程,多线程之间会共享内存,那么在修改共享内存数据的时候,需要通过加锁来保证数据的安全,而这样就会降低性能。而使用子进程,创建子进程时,父子进程是共享内存数据的,不过这个共享的内存只能以只读的方式,而当父子进程任意一方修改了该共享内存,就会发生「写时复制」,于是父子进程就有了独立的数据副本,就不用加锁来保证数据安全。

  1. 重写时,主进程会fork出一个子进程,由子进程将这些Redis数据写入重写日志

  2. 在子进程进行重写期间,主进程也会将新的写操作写入到AOF重写缓冲区中

  3. 当新的AOF文件创建完毕,Redis就会将重写缓冲区的内容追加到新的AOF文件,新AOF文件替换旧AOF文件

所以,尽管重写过程中有新数据写入,通过追加AOF重写缓冲的方式到新文件,依然可以保证不会丢失最新的写入操作

AOF混合持久化

是什么

混合持久化发生在原有的AOF流程。开启了混合持久化,在AOF重写日志的时候,fork出来的子进程会将内存数据以RDB的方式写入新的AOF文件中,期间主进程将写操作命令写入重写缓冲区中,最后追加到新的AOF文件中。

此时的AOF文件前半部分是RDB格式的全量数据,后半部分是AOF格式的增量数据。

优势

既有RDB文件小,加载快的优势,也有AOF持久化数据损失少的优势。但因为含有二进制数据,可读性会差一些。

服务启动时如何加载数据

混合持久文件里有REDIS这个标记,加载时能通过这个标记进行判断是否开启了混合持久化。

可以通过配置文件开启,5.0之后默认是打开的,所以5.0之后只要AOF配置开启,默认就是混合持久化。





Copyright 571400.cn.Some Rights Reserved.琼ICP备20000547号