Redis持久化 - RDB和AOF
1. 什么是持久化
持久化(Persistence),即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。 持久化Redis所有数据保持在内存中,对数据的更新将异步地保存到磁盘上。
2. 持久化的实现方式
快照方式持久化
快照方式持久化就是在某时刻把所有数据进行完整备份。
例:Mysql的Dump方式、Redis的RDB方式。
写日志方式持久化
写日志方式持久化就是把用户执行的所有写指令(增删改)备份到文件中,还原数据时只需要把备份的所有指令重新执行一遍即可。
例:Mysql的Binlog、Redis的AOF、Hbase的HLog。
二、RDB
1. 什么是RDB
RDB简介
RDB持久化方式能够在指定的时间间隔能对你的数据进行快照存储。 在默认情况下, Redis 将数据库快照保存在名字为 dump.rdb的二进制文件中。 在 Redis 运行时, RDB 程序将当前内存中的数据库快照保存到磁盘文件中, 在 Redis 重启动时, RDB 程序可以通过载入 RDB 文件来还原数据库的状态。
工作方式
当 Redis 需要保存 dump.rdb 文件时, 服务器执行以下操作:
- Redis 调用forks。同时拥有父进程和子进程。
- 子进程将数据集写入到一个临时 RDB 文件中。
- 当子进程完成对新 RDB 文件的写入时,Redis 用新 RDB 文件替换原来的 RDB 文件,并删除旧的 RDB 文件。
这种工作方式使得 Redis 可以从写时复制(copy-on-write)机制中获益。
2. RDB的三种主要触发机制
save命令(同步数据到磁盘上)
save
命令执行一个同步操作,以RDB文件的方式保存所有数据的快照。
127.0.0.1:6379> save
OK
由于 save
命令是同步命令,会占用Redis的主进程。若Redis数据非常多时,save
命令执行速度会非常慢,阻塞所有客户端的请求。
因此很少在生产环境直接使用SAVE 命令,可以使用BGSAVE 命令代替。如果在BGSAVE命令的保存数据的子进程发生错误的时,用 SAVE命令保存最新的数据是最后的手段。
bgsave命令(异步保存数据到磁盘上)
bgsave
命令执行一个异步操作,以RDB文件的方式保存所有数据的快照。
127.0.0.1:6379> bgsave
Background saving started
Redis使用Linux系统的fock()
生成一个子进程来将DB数据保存到磁盘,主进程继续提供服务以供客户端调用。
如果操作成功,可以通过客户端命令LASTSAVE来检查操作结果。
save
与 bgsave
对比
命令 |
save |
bgsave |
---|---|---|
IO类型 |
同步 |
异步 |
阻塞? |
是 |
是(阻塞发生在fock(),通常非常快) |
复杂度 |
O(n) |
O(n) |
优点 |
不会消耗额外的内存 |
不阻塞客户端命令 |
缺点 |
阻塞客户端命令 |
需要fock子进程,消耗内存 |
自动生成RDB
除了手动执行 save
和 bgsave
命令实现RDB持久化以外,Redis还提供了自动自动生成RDB的方式。
你可以通过配置文件对 Redis 进行设置, 让它在“ N 秒内数据集至少有 M 个改动”这一条件被满足时, 自动进行数据集保存操作。 比如说, 以下设置会让 Redis 在满足“ 60 秒内有至少有 1000 个键被改动”这一条件时, 自动进行数据集保存操作:
save 60 1000
3. RDB相关配置
# RDB自动持久化规则
# 当 900 秒内有至少有 1 个键被改动时,自动进行数据集保存操作
save 900 1
# 当 300 秒内有至少有 10 个键被改动时,自动进行数据集保存操作
save 300 10
# 当 60 秒内有至少有 10000 个键被改动时,自动进行数据集保存操作
save 60 10000
# RDB持久化文件名
dbfilename dump-<port>.rdb
# 数据持久化文件存储目录
dir /var/lib/redis
# bgsave发生错误时是否停止写入,通常为yes
stop-writes-on-bgsave-error yes
# rdb文件是否使用压缩格式
rdbcompression yes
# 是否对rdb文件进行校验和检验,通常为yes
rdbchecksum yes
4. RDB的优点
- RDB是一个非常紧凑的文件,它保存了某个时间点的数据集,非常适用于数据集的备份,比如你可以在每个小时报保存一下过去24小时内的数据,同时每天保存过去30天的数据,这样即使出了问题你也可以根据需求恢复到不同版本的数据集。
- RDB是一个紧凑的单一文件,很方便传送到另一个远端数据中心或者亚马逊的S3(可能加密),非常适用于灾难恢复。
- RDB在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他IO操作,所以RDB持久化方式可以最大化redis的性能。
- 与AOF相比,在恢复大的数据集的时候,RDB方式会更快一些。
5. RDB的缺点
- 耗时、耗性能。RDB 需要经常fork子进程来保存数据集到硬盘上,当数据集比较大的时候,fork的过程是非常耗时的,可能会导致Redis在一些毫秒级内不能响应客户端的请求。如果数据集巨大并且CPU性能不是很好的情况下,这种情况会持续1秒,AOF也需要fork,但是你可以调节重写日志文件的频率来提高数据集的耐久度。
- 不可控、丢失数据。如果你希望在redis意外停止工作(例如电源中断)的情况下丢失的数据最少的话,那么RDB不适合你。虽然你可以配置不同的save时间点(例如每隔5分钟并且对数据集有100个写的操作),是Redis要完整的保存整个数据集是一个比较繁重的工作,你通常会每隔5分钟或者更久做一次完整的保存,万一在Redis意外宕机,你可能会丢失几分钟的数据。
三、AOF
1. 什么是AOF
快照功能(RDB)并不是非常耐久(durable):如果 Redis 因为某些原因而造成故障停机, 那么服务器将丢失最近写入、且仍未保存到快照中的那些数据。从 1.1 版本开始, Redis 增加了一种完全耐久的持久化方式:AOF 持久化。 你可以在配置文件中打开AOF方式:
appendonly yes
打开AOF后, 每当 Redis 执行一个改变数据集的命令时(比如 SET), 这个命令就会被追加到 AOF 文件的末尾。这样的话, 当 Redis 重新启时, 程序就可以通过重新执行 AOF 文件中的命令来达到重建数据集的目的。
AOF运行原理 - 创建
AOF运行原理 - 恢复
2. AOF持久化的三种策略
你可以通过配置文件配置 Redis 多久才将数据 fsync 到磁盘一次。
always
每次有新命令追加到 AOF 文件时就执行一次 fsync :非常慢,也非常安全。
everysec
每秒 fsync 一次:足够快(和使用 RDB 持久化差不多),并且在故障时只会丢失 1 秒钟的数据。 推荐(并且也是默认)的措施为每秒 fsync 一次, 这种 fsync 策略可以兼顾速度和安全性。
no
从不 fsync :将数据交给操作系统来处理,由操作系统来决定什么时候同步数据。更快,也更不安全的选择。
always、everysec、no对比
命令 |
优点 |
缺点 |
---|---|---|
always |
不丢失数据 |
IO开销大,一般SATA磁盘只有几百TPS |
everysec |
每秒进行与fsync,最多丢失1秒数据 |
可能丢失1秒数据 |
no |
不用管 |
不可控 |
推荐(并且也是默认)的措施为每秒 fsync 一次, 这种 fsync 策略可以兼顾速度和安全性。
3. AOF重写
因为 AOF 的运作方式是不断地将命令追加到文件的末尾, 所以随着写入命令的不断增加, AOF 文件的体积也会变得越来越大。举个例子, 如果你对一个计数器调用了 100 次 INCR , 那么仅仅是为了保存这个计数器的当前值, AOF 文件就需要使用 100 条记录(entry)。然而在实际上, 只使用一条 SET 命令已经足以保存计数器的当前值了, 其余 99 条记录实际上都是多余的。 为了处理这种情况, Redis 支持一种有趣的特性:可以在不打断服务客户端的情况下, 对 AOF 文件进行重建(rebuild)。执行 bgrewriteaof 命令, Redis 将生成一个新的 AOF 文件, 这个文件包含重建当前数据集所需的最少命令。 Redis 2.2 需要自己手动执行 bgrewriteaof 命令;Redis 2.4 则可以通过配置自动触发 AOF 重写。
AOF重写的作用
- 减少磁盘占用量
- 加速数据恢复
AOF重写的实现方式
- bgrewriteaof 命令
Redis bgrewriteaof 命令用于异步执行一个 AOF(AppendOnly File)文件重写操作。重写会创建一个当前AOF文件的体积优化版本。
即使 bgrewriteaof 执行失败,也不会有任何数据丢失,因为旧的AOF文件在 bgrewriteaof 成功之前不会被修改。
AOF 重写由 Redis 自行触发,bgrewriteaof 仅仅用于手动触发重写操作。
具体内容:
- 如果一个子Redis是通过磁盘快照创建的,AOF重写将会在RDB终止后才开始保存。这种情况下BGREWRITEAOF仍然会返回OK状态码。从Redis 2.6起你可以通过INFO命令查看AOF重写执行情况。
- 如果只在执行的AOF重写返回一个错误,AOF重写将会在稍后一点的时间重新调用。
- AOF重写配置
配置名 |
含义 |
---|---|
auto-aof-rewrite-min-size |
触发AOF文件执行重写的最小尺寸 |
auto-aof-rewrite-percentage |
触发AOF文件执行重写的增长率 |
统计名 |
含义 |
---|---|
aof_current_size |
AOF文件当前尺寸(字节) |
aof_base_size |
AOF文件上次启动和重写时的尺寸(字节) |
AOF重写自动触发机制,需要同时满足下面两个条件:
- aof_current_size > auto-aof-rewrite-min-size
- (aof_current_size - aof_base_size) * 100 / aof_base_size > auto-aof-rewrite-percentage
假设 Redis 的配置项为:
auto-aof-rewrite-min-size 64mb
auto-aof-rewrite-percentage 100
当AOF文件的体积大于64Mb,并且AOF文件的体积比上一次重写之久的体积大了至少一倍(100%)时,Redis将执行 bgrewriteaof 命令进行重写。
AOF重写的流程
3. AOF相关配置
# 开启AOF持久化方式
appendonly yes
# AOF持久化文件名
appendfilename appendonly-<port>.aof
# 每秒把缓冲区的数据同步到磁盘
appendfsync everysec
# 数据持久化文件存储目录
dir /var/lib/redis
# 是否在执行重写时不同步数据到AOF文件
# 这里的 yes,就是执行重写时不同步数据到AOF文件
no-appendfsync-on-rewrite yes
# 触发AOF文件执行重写的最小尺寸
auto-aof-rewrite-min-size 64mb
# 触发AOF文件执行重写的增长率
auto-aof-rewrite-percentage 100
4. AOF的优点
- 使用AOF 会让你的Redis更加耐久: 你可以使用不同的fsync策略:无fsync,每秒fsync,每次写的时候fsync。使用默认的每秒fsync策略,Redis的性能依然很好(fsync是由后台线程进行处理的,主线程会尽力处理客户端请求),一旦出现故障,你最多丢失1秒的数据。
- AOF文件是一个只进行追加的日志文件,所以不需要写入seek,即使由于某些原因(磁盘空间已满,写的过程中宕机等等)未执行完整的写入命令,你也也可使用redis-check-aof工具修复这些问题。
- Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写:重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。整个重写操作是绝对安全的,因为 Redis 在创建新 AOF 文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。而一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。
- AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松。导出(export) AOF 文件也非常简单:举个例子, 如果你不小心执行了 FLUSHALL 命令, 但只要 AOF 文件未被重写, 那么只要停止服务器, 移除 AOF 文件末尾的 FLUSHALL 命令, 并重启 Redis , 就可以将数据集恢复到 FLUSHALL 执行之前的状态。
5. AOF的缺点
- 对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。
- 根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB 。在一般情况下, 每秒 fsync 的性能依然非常高, 而关闭 fsync 可以让 AOF 的速度和 RDB 一样快, 即使在高负荷之下也是如此。不过在处理巨大的写入载入时,RDB 可以提供更有保证的最大延迟时间(latency)。
四、RDB和AOF的抉择
1. RDB 和 AOF 对比
- |
RDB |
AOF |
---|---|---|
启动优先级 |
低 |
高 |
体积 |
小 |
大 |
恢复速度 |
快 |
慢 |
数据安全性 |
丢数据 |
根据策略决定 |
2. 如何选择使用哪种持久化方式?
一般来说, 如果想达到足以媲美 PostgreSQL 的数据安全性, 你应该同时使用两种持久化功能。
如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失, 那么你可以只使用 RDB 持久化。
有很多用户都只使用 AOF 持久化, 但并不推荐这种方式:因为定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快。
source:https://segmentfault.com/a/1190000016021217
- 远程RPC溢出EXP编写实战之MS06-040
- 浮点数加法引发的问题:浮点数的二进制表示
- 新手科普 | MySQL手工注入之基本注入流程
- linux 系统监控、诊断工具之 lsof 用法简介
- 关于 SimpleDateFormat 的非线程安全问题及其解决方案
- 关于 WEB/HTTP 调试利器 Fiddler 的一些技巧分享
- Java线程使用技巧学习(一)
- Python FAQ(常见问题解答)(1)
- ElastAlert监控日志告警Web攻击行为
- Java线程使用技巧学习(二)
- 挖洞经验 | 看我如何发现“小火车托马斯”智能玩具APP聊天应用漏洞
- Hive 常见问题与技巧【Updating】
- Hive 基础(1):分区、桶、Sort Merge Bucket Join
- 简化你的 java 字符串操作:Guava 之 CharMatcher 用法简介
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 冒泡排序的实现思路和优化方案
- Java Map转对象
- 59.Vue 使用webpack构建vue项目
- Android初学设置文字跑马灯效果
- 使用Zolom内存解析运行python脚本(不落地)
- 要点3:输入函数对比与自定义输入方式
- 性能测试必备命令(3)- lscpu
- 性能测试必备命令(2)- uptime
- Lua/luajit 点与冒号的区别
- vim的几种模式mode和按键映射map
- PHP parent 的注意点
- 不停服务调试(debug)线上Rsyslog
- 使用ulimit 命令、/etc/security/limits.conf、proc 调整系统参数
- 解决jupyter notebook matplotlib绘图中文乱码问题
- 【动手学深度学习笔记】之过拟合与欠拟合实例