REDIS持久化的两种方式--RDB和AOF详解

REDIS持久化的两种方式

  • RDB
    • 命令
    • RDB文件
    • RDB优缺点
  • AOF
    • 开启使用
    • 命令写入AOF文件格式
    • 文件同步策略
    • 重写机制
    • 重启加载机制
  • 问题优化和定位
    • fork操作
    • fork耗时问题定位
    • 子进程开销
      • cpu
      • 内存
      • 硬盘
    • AOF追加阻塞

RDB

将数据生成快照保存在磁盘

命令

bgsave:fork操作创建子进程,阻塞时间短
在这里插入图片描述
触发方式:

  1. redis配置文件/etc/redis.conf中设置自动持久化


save 900 1 表示每900秒有一次修改就自动bgsave
3. 从节点执行全量复制时,主节点自动bgsave生成RDB文件发送给从节点
4. 执行debug reload时也会触发save操作
5. 默认情况下,执行shutdown时,如果没有开启AOF持久化就会自动执行bgsave进RDB持久化

bgsave流程说明:

  1. 父进程判断是否存在子进程在进行持久化,如果存在就退出
  2. fork操作,产生子进程后返回:background saving started。父进程已经释放。latest_fork_usec是最近一次fork操作的耗时,单位(微妙)
  3. 子进程开始创建RDB文件:lastsave 可以看到最后一次save的时间
  4. 结束后子进程向父进程发送信号,父进程统计信息更新info persistence

save:阻塞redis,已经废弃
在这里插入图片描述

RDB文件

  1. 存储位置:config get dir; config get dbfilename
  2. 压缩存储建议开启:config set rdbcompression yes
  3. 校验文件是否损坏:工具redis-check-dump

RDB优缺点

紧凑压缩的二进制文件,加载快,适用于灾备。但是使用子进程是一个重量级操作不适合实时持久化,而且老版本无法兼容。故有了AOF(append only file)持久化方式

AOF

以独立日志的方式记录下每一个写命令,重启时在重新执行。解决了redis数据持久化的实时性问题。

开启使用

开启:config set appendonly yes
文件名:config get dir ;config get appendfilename; 默认为appendonly.aof

流程

  1. 所有的写入命令会追加到aof_buf缓冲区中
  2. AOF缓冲区根据相应策略向硬盘做出同步操作
  3. 需要对aof文件定期重写压缩空间
  4. redis服务器重启时加载用于数据恢复

命令写入AOF文件格式

文本协议格式

例如:set hello world
写入AOF文件内:*3\r\n$3\r\nset\r\n$5\r\n\hello\r\n$5\r\nworld\r\n

优点:可读,便于修改;避免二次处理;兼容性好

文件同步策略

由参数appendfsync控制

  1. always:命令写入buf后调用系统fsync操作同步到AOF文件,完成后线程返回。(不建议)
  2. everysec:写入aof_buf后调用write操作写入缓冲区后即返回。每秒钟调用一次fsync操作。(默认,最多丢失2s的数据)
  3. no:写入aof_buf后调用write操作写入缓冲区后即返回。同步到硬盘的工作由操作系统进行,通常是30s。(安全性无法保证,不建议)

fsync操作:强制同步到硬盘,写入硬盘后线程返回,保证了持久化。

write操作:写入文件缓冲区页后即返回,缓冲区写满或者达到特定条件后进行持久化。如果中间发生宕机,缓冲区页内的数据可能会丢失。

重写机制

将进程内的数据转化为写命令重新写入AOF文件,压缩AOF文件大小,能够更快的加载。

触发方式:
手动触发:bgrewriteaof
自动触发:根据参数:auto-aof-rewrite-min-size(运行AOF重写时的aof文件最小体积)和auto-aof-rewrite-percentage(当前aof文件体积aof_current_size和上一次文件重写后的体积aof_base_size的比值)参数确定触发时机

当aof-current-size>auto-aof-rewrite-min-size
且(aof_current_size - aof_base_size)/aof_base_size>=auto-aof-rewrite-percentage

ps: aof_current_size 和 aof_base_size通过info persistence查看

重写流程:

  1. 父进程查看是否由fork操作,如无则创建子进程
  2. 父进程返回响应其他命令,所有操作记录进入AOF缓冲区,并根据appendfsync策略同步到硬盘;子进程的新数据写入AOF重写缓冲区
  3. 子进程根据内存快照将命令合并规则写入到新的AOF文件,每次批量写入硬盘,根据参数aof-rewrite-incremental-fsync控制,默认32M。
  4. 新AOF文件写入完成后,父进程更新info统计信息
  5. 父进程把AOF文件重写缓冲区中的数据写入到新的AOF文件中
  6. 替换新旧文件,完成重写。

重启加载机制

优先加载AOF文件,没有的话再看RDB文件

文件校验:

对于错误的文件无法加载时可以备份后进行修复:redis-check-aof --fix

掉电等故障可能会导致aof文件尾部写入不全。参数aof-load-truncated用来兼容此情况,默认开启,如果遇到此问题忽略并继续加载,打印警告日志。

问题优化和定位

fork操作

fork操作会复制父进程的空间内存页表。例如10G的redis进程需要复制约20M的内存页表。故fork操作耗时跟进程的总内存量息息相关。

fork耗时问题定位

正常情况下fork操作每GB耗时20ms左右。
查看上一次fork操作耗时:info stats 中latest_fork_usec(单位:微秒)

如何改善fork操作耗时:

  1. 使用物理机,避免使用Xen
  2. 控制redis实例最大可用内存,建议10G以内。
  3. 降低fork频率,适度放宽AOF自动触发时机,避免不必要的全量复制。

子进程开销

cpu

  1. redis 是cpu密集型服务,子进程可以消耗掉单核cpu的90%,所以不要做cpu的单核绑定。
  2. 保证只有一个子进程执行重写

内存

子进程通过fork操作产生,占用内存大小等同于父进程,理论上需要2倍内存来完成持久化操作。但是linux有写时复制机制(copy-on-write),
父子进程共享相同的物理内存页,父进程处理写请求时会把修改的内存页创建副本,子进程在fork过程中共享父进程整内存快照。

重写内存消耗情况:
监控redis日志
RDB重写:copy-on-write父进程创建的副本内存
AOF重写:copy-on-write父进程创建的副本内存+AOF缓冲区所占内存

内存消耗优化:

  1. 尽量保证只有一个子进程在工作
  2. 避免大量写入时做子进程重写工作,会导致父进程创建的内存页副本过多,内存消耗大
  3. linux有TransparentHugePage(默认开启),大页2M,当开启时复制页会从4k变到2M,会大幅增加重写期间的内存消耗。
    关闭方式:sudo echo never > /sys/kernel/mm/transaparent_hugepage/enabled

硬盘

持久化到硬盘,硬盘写入压力,命令:sar/iostat/iotop 分析当前的硬盘负载情况。
优化方式:

  1. 不要和其他高硬盘负载的服务部署在一起,如存储服务、消息队列
  2. 开启配置:no-appendfsync-on-rewrite,表示重写期间不开启fsycnc操作
  3. 当开启AOF功能的redis并且应对高流量写入场景时,普通机械磁盘的吞吐在100MB/s。瓶颈是AOF同步硬盘上
  4. 如果是多redis实例,可以将AOF文件存储在不同的盘上,分摊写入压力

AOF追加阻塞

AOF持久化常用的硬盘同步策略是everysec,如果系统硬盘资源繁忙,会造成redis主线程的阻塞。
主线程每隔1秒进行一次对比,如果距离上次同步成功时间>2s,堵塞主线程
所以

  1. everysec可能会丢失2s的数据
  2. 硬盘资源的繁忙可能会导致redis主线程的堵塞

问题定位:

  1. 发生AOF阻塞时,redis日志中会输出相关内容
  2. 发生阻塞时,info persistence的统计中aof_delayed_fsync指标会累加
  3. iotop 可以看出硬盘的高负载问题

参考:《redis开发与运维》付磊、张益军著

你可能感兴趣的:(redis,缓存,数据库)