首页 > 数据库 >Redis 持久化机制

Redis 持久化机制

时间:2023-04-06 21:47:48浏览次数:54  
标签:AOF 持久 Redis 命令 RDB 进程 重写 化机制

目录

Redis数据持久化

Redis提供了四种持久化策略:RDB (Redis Database)、AOF、RDB + AOF 和 不持久化。

  • RDB(Redis Database)

    在指定的时间间隔内将内存中的数据集以快照形式写入磁盘。

    • 优点:

      • RDB 快照是一个压缩过的非常紧凑的文件,保存着某个时间点的数据集,适合做数据的备份,灾难恢复;

      • 可以最大化Redis的性能,在保存RDB文件,服务器进程只需fork一个子进程来完成RDB文件的创建,父进程不需要做IO操作;

      • 与AOF相比,恢复大数据集的时候会更快。

    • 缺点:

      • RDB 的数据安全性不如 AOF,生成两次快照之间的发生故障,存在数据丢失风险。

      • RDB 需要经常 fork() 以便使用子进程持久保存在磁盘上。如果数据集很大,fork() 可能会很耗时,可能会导致 Redis 停止为客户端提供几毫秒甚至一秒的服务。

  • AOF(Append Only File)

    AOF 持久化记录服务器收到的每一个写操作。然后可以在服务器启动时再次重放这些操作,重建原始数据集。使用与 Redis 协议本身相同的格式记录命令。

    • 优点:

      • 数据更完整,安全性更高,秒级数据丢失(取决fsync策略,如果是everysec,最多丢失1秒的数据);
      • AOF文件是一个只进行追加的日志文件,且写入操作是以Redis协议的格式保存的,内容是可读的,适合误删紧急恢复。
    • 缺点:

      • 对于相同的数据集,AOF文件的体积要大于RDB文件,数据恢复也会比较慢;
      • 根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB。 不过在一般情况下, 每秒 fsync 的性能依然非常高。
  • RDB + AOF

    同时使用AOF 和 RDB 两种持久化组合。

  • 不持久性

    一般作为缓存的时候会用到。

RDB(Redis Database)

RDB 持久化是以指定的时间间隔执行,将内存中数据集的时间点快照写入磁盘。

在创建快照之后,用户可以备份该快照,可以将快照复制到其他服务器以创建相同数据的服务器副本,或者在重启服务器后恢复数据。

RDB 文件是一个压缩过的文件,因此RDB持久化的体积比AOF小,因为存储的是数据,所以恢复很快,性能好,但是可能会丢失最后一次持久化的数据。RDB是Redis默认的持久化方式。

触发快照的时机

触发快照的时机:

  • 执行 save 和 bgsave 命令;

    • save命令:在主进程生成 RDB 文件,由于和执行操作命令在同一个线程,所以,如果写入 RDB 文件的时间太长,会阻塞主线程。

    • bgsave命令:fork()一个子进程,由子进程负责创建RDB文件,Redis主进程继续处理操作命令,这样可以避免主线程的阻塞。

  • 配置文件的选项设置 save <seconds> <changes> 规则,自动间隔性执行 bgsave 命令;

    例如,60秒内,对数据库进行了至少10000次修改:

    save 60 10000
    

    注意,执行快照是一个比较重的操作,如果频率太频繁,可能会对 Redis 性能产生影响。

  • 主从复制时,从库全量复制同步主库数据,主库会执行bgsave;

  • 执行flushall命令清空服务器数据;

  • 执行shutdown命令关闭Redis时,会执行save命令;

Redis 提供了两个命令来生成 RDB 文件,分别是 save 和 bgsave,他们的区别就在于是否在「主线程」里执行:

执行了 save 命令,就会在主线程生成 RDB 文件,由于和执行操作命令在同一个线程,所以如果写入 RDB 文件的时间太长,会阻塞主线程;
执行了 bgsave 命令,会创建一个子进程来生成 RDB 文件,这样可以避免主线程的阻塞;

AOF(Append Only File)

Redis 首先执行写操作命令,然后,再将该命令记录到 AOF 日志里,过程如下:

image

这样的优缺点如下:

  • 优点

    • 避免额外的检查开销

      如果当前执行的命令语法有问题,如果没有对其进行语法检查,这条错误的命令记录到 AOF 日志里后,在 Redis 在使用日志恢复数据时,就可能会出错。

    • 不会阻塞当前写操作命令的执行

  • 缺点

    • 存在数据丢失的风险

      当 Redis 在还没来得及将命令写入到磁盘时,如果服务器宕机,此时,数据就会有丢失的风险。

    • 存在阻塞后续命令执行的风险

      由于写操作命令执行成功后才记录到 AOF 日志,所以,不会阻塞当前写操作命令的执行,但是,可能会阻塞后续命令的执行。

      image

      执行命令将命令写入日志两个步骤,在主进程里面是串行执行的。

AOF 日志写入过程

Linux允许把脏缓冲区写入块设备的操作延迟执行(write back),这样可以显著提高系统的性能。

Redis 写入 AOF 日志的过程,如下图:

image

可以看到,Redis 每执行一个写命令,都会把该命令以协议格式先追加到 server.aof_buf 缓存区的末尾,而不是直接写入文件,避免每次有命令都直接写入硬盘,减少磁盘 I/O 操作次数

AOF 记录 Redis 的每个写命令的步骤为:命令追加(append)、文件写入(write)和文件同步(sync),具体步骤如下:

  • Redis 执行完写操作命令后,会将命令追加(append)到 server.aof_buf 缓冲区;

  • 执行通过 write() 系统调用,将 server.aof_buf 缓冲区的数据写入到 AOF 文件,此时,数据还在内核 page cache 缓冲区,没有写入到磁盘,等待内核将数据写入硬盘;

  • 操作系统将 page cache 缓冲区中的内容,刷新入 AOF文件。

对于何时把 server.aof_buf 缓冲区的内容写入保存在 AOF 文件中,Redis提供了三种策略:

  • appendfsync always:每次写操作命令执行完后,同步执行 fsync() 系统调用,将 AOF 日志数据写回硬盘;

  • appendfsync everysec:每次写操作命令执行完后,先将命令写入到 AOF 文件的内核缓冲区,然后每隔一秒执行 fsync() 系统调用,将缓冲区里的内容写回到磁盘,该操作由一个异步线程专门负责;

  • appendfsync no:每次写操作命令执行完后,只把命令写入到 AOF 文件的内核缓冲区,操作系统控制写回的时机。

三种数据刷盘策略的优缺点:

策略 写回时机 优点 缺点
always 同步写回 可靠性高、最大程度保证数据不丢失 性能最差,每个命令都会写回磁盘
everysec 每秒写回 性能适中 宕机时,会有1秒内数据丢失风险
no 由操作系统控制 性能最好 宕机时,会有大量数据丢失的风险

AOF 重写机制

随着服务器运行时间的流逝,可能会频繁的对同一个 key 进行很多写操作,AOF 文件中的内容会越来越多,文件越来越大,如果不加以控制,会对服务器的性能造成影响,还原数据的时间也加长。

Redis 支持一个有趣的特性:它能够在后台重建 AOF,而不会中断对客户端的服务。每当我们手动执行 BGREWRITEAOF 命令时,Redis 都会重新记录在内存中重建当前数据集所需的最短命令序列

AOF 重写

执行 bgrewriteaof 命令,重写的流程如下:

  • Redis 主进程会创建(fork)一个“重写”子进程(bgrewriteaof),这个子进程会首先读取现有的 AOF 文件,并将其包含的指令进行分析压缩并写入到一个临时文件中。

  • 在重写期间,Redis 主进程继续处理命令请求,如果有写入的命令,会追加到 AOF 缓冲区(aof_buf) ,同时还会追加到 AOF 重写缓冲区(aof_rewrite_buf )

  • 当子进程完成重写工作后,它会给父进程发一个信号,父进程会把 AOF 重写缓冲区的内容写进新的 AOF 临时文件中。

  • Redis 就会用新 AOF 文件来代替旧 AOF 文件,这样可以保证新的AOF文件与当前数据库数据的一致性。

通过 AOF 重写机制,Redis 会创建一个新的 AOF 文件替代现有的 AOF 文件,新旧两个文件保存的数据库状态相同,但是,新的 AOF 文件不会包含浪费空间的冗余命令,只保存必须的命令,因此新的 AOF 体积就要小得多。

这样,即使某个键值对被多条写命令反复修改,最终,也只需要用一条命令去记录这个键值对当前的最新状态,以代替之前记录这个键值对的多次修改记录,这样就减少了 AOF 文件中的命令数量。

同时,为了避免重写过程对现有的 AOF 文件会造成污染,所以,需要先创建一个新的 AOF 文件,待重写完成后,再将新的 AOF 文件替换现有的 AOF 文件。

主进程在通过 fork 系统调用生成 bgrewriteaof 子进程时,操作系统会把主进程的页表复制一份给子进程,这个页表记录着虚拟地址和物理地址映射关系,而不会复制物理内存,也就是说,两者的虚拟空间不同,但其对应的物理空间是同一个。

image

可以看到,当操作系统复制父进程页表的时候,父进程是处于阻塞中的状态,但是,由于页表的大小相比实际的物理内存小很多,所以,通常复制页表的过程是比较快的。但是,如果父进程的内存数据非常大,那页表也会很大,此时,父进程在通过 fork 创建子进程的时候,阻塞的时间也会越久。

这样,子进程就共享了父进程的物理内存数据了,能够节约物理内存资源,页表对应的页表项的属性会标记该物理内存的权限为只读。

当父进程或者子进程在向这个内存发起写操作时,CPU 就会触发写保护中断,这个写保护中断是由于违反权限导致的,然后操作系统会在写保护中断处理函数里进行物理内存的复制,并重新设置其内存映射关系,将父子进程的内存读写权限设置为可读写,最后才会对内存进行写操作,这个过程被称为写时复制(Copy On Write)

写时复制

只有在发生写操作的时候,操作系统才会去复制物理内存,这样是为了防止 fork 创建子进程时,由于物理内存数据的复制时间过长而导致父进程长时间阻塞的问题。

image

所以,有两个阶段会导致阻塞父进程:

  • 创建子进程的过程中,由于要复制父进程的页表等数据结构,阻塞的时间跟页表的大小有关,页表越大,阻塞的时间也越长;
  • 创建完子进程后,如果子进程或者父进程修改了共享数据,就会发生写时复制,这期间会拷贝物理内存,如果内存越大,自然阻塞的时间也越长。

AOF 重写缓冲区

在重写 AOF 期间,当 Redis 执行完一个写命令之后,它会同时将这个写命令写入到 AOF 缓冲区AOF 重写缓冲区,如下图所示:

在 bgrewriteaof 子进程执行 AOF 重写期间,主进程需要执行以下三个步骤:

  • 执行客户端发来的命令;

  • 将执行后的写命令追加到 AOF 缓冲区

  • 将执行后的写命令追加到 AOF 重写缓冲区

这样,就避免了重写 AOF 日志过程中,主进程修改了已经存在 key-value,导致这个 key-value 数据在子进程的内存数据跟主进程的内存数据不一致。

当子进程完成 AOF 重写工作(扫描数据库中所有数据,逐一把内存数据的键值对转换成一条命令,再将命令记录到重写日志)后,会向主进程发送一条信号,信号是进程间通讯的一种方式,且是异步的。

子进程重写的优点

通过后台线程执行重写操作,有两个好处:

  • 子进程进行 AOF 重写期间,主进程可以继续处理命令请求,从而避免阻塞主进程。

  • 子进程带有主进程的数据副本,不会导致线程安全问题。

    如果是使用线程,多线程之间会共享内存,那么,在修改共享内存数据的时候,需要通过加锁来保证数据的安全,而这样就会降低性能。

    而创建子进程时,父子进程是共享内存数据的,不过这个共享的内存只能以只读的方式,而当父子进程任意一方修改了该共享内存,就会发生写时复制(Copy On Write),于是父子进程就有了独立的数据副本,就不用加锁来保证数据安全。

总结

触发重写机制后,主进程就会创建重写 AOF 的子进程,此时父子进程共享物理内存,重写子进程只会对这个内存进行只读,重写 AOF 子进程会读取数据库里的所有数据,并逐一把内存数据的键值对转换成一条命令,再将命令记录到重写日志(新的 AOF 文件)。

子进程重写过程中,主进程依然可以正常处理命令,如果此时主进程修改了已经存在 key-value,就会发生写时复制,注意这里只会复制主进程修改的物理内存数据,没修改物理内存还是与子进程共享的。

RDB + AOF 组合

Redis 4.0 开始支持将 RDB 和 AOF 组合使用,该方法叫混合使用 AOF 日志和内存快照,也叫混合持久化。通过如下配置项设置:

aof-use-rdb-preamble yes

当开启了混合持久化时,在 AOF 重写日志时,fork 出来的重写子进程会先将与主线程共享的内存数据以 RDB 方式写入到 AOF 文件,然后主线程处理的操作命令会被记录在重写缓冲区里,重写缓冲区里的增量命令会以 AOF 方式写入到 AOF 文件,写入完成后通知主进程将新的含有 RDB 格式和 AOF 格式的 AOF 文件替换旧的的 AOF 文件。

image

也就是说,使用了混合持久化,AOF 文件的前半部分是 RDB 格式的全量数据,后半部分是 AOF 格式的增量数据。

这样的好处在于,重启 Redis 加载数据的时候,由于前半部分是 RDB 内容,这样加载的时候速度会很快。

加载完 RDB 的内容后,才会加载后半部分的 AOF 内容,这里的内容是 Redis 后台子进程重写 AOF 期间,主线程处理的操作命令,可以使得数据更少的丢失。

参考:

标签:AOF,持久,Redis,命令,RDB,进程,重写,化机制
From: https://www.cnblogs.com/larry1024/p/17285920.html

相关文章

  • Spring Cache使用方式——不用默认,使用redis进行缓存
    在SpringBoot项目中使用SpringCache的操作步骤(使用redis缓存技术)1、导入Maven坐标spring-boot-starter-data-redis、sping-boot-starter-cache2、配置application.ymlspring:cache:redis:time-to-live:1800000#设置缓存......
  • redis基础数据结构详解
    一.redis为什么快基于内存的存储虽然是单线程,但是采取了多路复用,可以高效的处理网络并发良好的数据结构设计二.redis基础数据结构redis有五种基础的数据结构string,list,set,zset,hashredis所有的数据结构的key都是string类型,我们所说的数据结构都是指value的数据结构......
  • 通过 Homebrew 在 Mac OS X 上安装和配置 Redis
    通过使用Homebrew,可以大大降低在MacOSX上设置和配置开发环境的成本。让我们安装Redis。$brewinstallredis安装后,我们将看到一些有关配置注意事项的通知。离开它并继续关注本文中的一些任务。开机自启动Redis$ln-sfv/usr/local/Cellar/redis/7.0.10/*.plist......
  • docker-compose 运行 redis
    redis.conflogfile/data/redis/logs/redis.logdir/data/redis/databind0.0.0.0requirepass123456UID#dockerrun--rm-itredis:7.0.10idredisuid=999(redis)gid=999(redis)groups=999(redis)docker-compose.yamlversion:'3.9'services:r......
  • redis集群,模块启动报错:PoolException: Returned connection io.lettuce.core.cluster.
    redis3主3从的配置启动正常,客户端命令使用正常,突然今天开发测试环境有些模块报错了:org.springframework.data.redis.connection.PoolException:Returnedconnectionio.lettuce.core.cluster.StatefulRedisClusterConnectionImpl@49bd0985waseitherpreviouslyreturnedor......
  • 【重要】Nginx模块Lua-Nginx-Module学习笔记(三)Nginx + Lua + Redis 已安装成功(非open
    一、目标使用Redis做分布式缓存;使用luaAPI来访问redis缓存;使用nginx向客户端提供服务,ngx_lua将lua嵌入到nginx,让nginx执行lua脚本,高并发,非阻塞的处理各种请求。url请求nginx服务器,然后lua查询redis,返回json数据。二、准备工作系统环境:Ubuntu14.0(64位)Redis服务安装:ap......
  • Redis核心知识之—— 时延问题分析及应对、性能问题和解决方法【★★★★★】...
     参考网址:Redis常见的性能问题和解决方法:http://www.searchdatabase.com.cn/showcontent_63439.htmRedis主从配置详细过程:http://sofar.blog.51cto.com/353572/861276 读后感:1、在架构设计中,有“分流”一招,说的是将处理快的请求和处理慢的请求分离来开,否则,慢的影响到了快的,让快的......
  • Redis基础知识之—— 缓存应用场景
    一、MySql+Memcached架构的问题Memcached采用客户端-服务器的架构,客户端和服务器端的通讯使用自定义的协议标准,只要满足协议格式要求,客户端Library可以用任何语言实现。Memcached服务器使用基于Slab的内存管理方式,有利于减少内存碎片和频繁分配销毁内存所带来的开销。各个Slab按需......
  • Redis实践操作之—— keyspace notification(键空间通知)
    源码地址:https://github.com/Tinywan/PHP_Experience一、需求分析:设置了生存时间的Key,在过期时能不能有所提示?如果能对过期Key有个监听,如何对过期Key进行一个回调处理?如何使用Redis来实现定时任务?二、序言:    本文所说的定时任务或者说计划任务并不是很多人想象中的那样,比......
  • Redis 在消息队列中的应用
    1.Redis的List数据类型1.1List数据类型的特点List列表是Redis提供的一种重要的数据类型。它是由若干个字符串元素组成的集合,并且每个字符串元素都是按照插入顺序排序的。也可以将列表理解为多个字符串组成的一个集合对象,并按照链表(LinkList)的插入顺序排序。在读......