Redis是内存服务器,而且数据时以键值对的形式存储。对于内存服务器来说,如果是数据库数据没有定时备份,一旦服务器出现故障或者停机,那么数据将会消失不见,这将是灾难级别的影响。为了解决这个问题,Redis提供了持久化机制RDB和AOF。
RDB持久化
基本操作流程:
RDB持久化是将内存中的数据以快照的形式保持在磁盘中,产生的是一个经过压缩的二进制文件文件(dump.rdp),这样一来如果服务器进程意外停止,数据就可以恢复到文件保存的节点。
实现方式:
RDB文件生成主要包SAVE和BGSAVE两个命令来实现。而这个两个方式的主要区别是,SAVE会阻塞Redis服务的进程,直到RDB文件创建完成为止,也就是说SAVE期间无法接受其他请求;而BGSAVE是在保证主进程的同时,会fork一个子进程来实现RDB文件,不会影响主进程的活动,当子进程完成写临时文件后,将原来的 RDB 替换掉,这样的好处是可以 copy-on-write。而RDB文件的载入是在Redis服务启动时只要检测到RDB文件存在就会自动载入。
自动间歇性保存
SAVE时会阻塞主进程,不是备份数据的首选。而BGSAVE执行时fork子进程,不影响客户端对主进程的请求。所以Resdis可以通过设置save参数来让服务器自动执行BGSAVE备份数据。redis.conf主要配置如下:
################################ SNAPSHOTTING ################################
#
# Save the DB on disk:
#
# save <seconds> <changes>
#
# Will save the DB if both the given number of seconds and the given
# number of write operations against the DB occurred.
#
# In the example below the behaviour will be to save:
# after 900 sec (15 min) if at least 1 key changed
# after 300 sec (5 min) if at least 10 keys changed
# after 60 sec if at least 10000 keys changed
#
# Note: you can disable saving completely by commenting out all "save" lines.
#
# It is also possible to remove all the previously configured save
# points by adding a save directive with a single empty string argument
# like in the following example:
#
# save ""
save 900 1
save 300 10
save 60 10000
满足以下三个条件:BGSAVE就会被执行:
- 服务器900秒之内,对数据库进行了至少1次修改;
- 服务器在300秒内,对数据库进行至少10次修改;
- 服务在60秒内,对数据库进行了至少1000次修改
Redis主要通过维护一个dirty计数器来保存对数据库修改的次数(修改次数是根据元素个数判断)和lastsave来记录执行保存的操作时间。在通过周期性的检查(每隔100ms)执行,来确认是否满足条件来执行BGSAVE操作。
RDB文件示例:
REDIS | db_version | databases | EOF | check_sum |
标志 | 长度4字节版本号 | 数据库 | 正文内容 | 校验数 (8个字节) |
REDIS 0006 :Redis版本号
\0 003 age 300 023: 0表示字符串类型的key,003表示key的长度,age是key ,300表示长度,023表示value
1 020 4 P G 232 211 350 :表示八字节长的校验和。
AOF持久化
基本操作流程:
AOF持久化与RDF不同的是,AOF是通过保存Redis服务所执行的写命令来记录数据库钻状态的。以纯文本的形式以Redis的命令请求协议格式保存,包括SET、SADD、RPUSH命令。
实现原理:
AOF持久化默认关闭的,需要修改redis.conf配置开启
############################## APPEND ONLY MODE ###############################
appendonly yes
AOF持久化实现分为三个步骤,命令追加,文件写入,文件同步。整个过程是一个一个轮循事件,命令追加是在有写的操作命令时,会以协议格式被追加到个aof_buf缓冲区的末尾。而文件写入和文件步骤是根据redis.conf的中的appendfsync来决定的。
void aof{
//追加到缓冲区
apend_aof_buf();
//处理时间事件
timeEvents();
//考虑是否写入同步(appendfsync来决定的)
flusshFile();
}
appendfsync的三种模式
############################## APPEND ONLY MODE ###############################
#always 服务器在每个事件循环都要将aof_buf缓冲区中的所有内容写入到AOF文件中,效率最慢,但是最安全,即使出现故障停机,AOF持续化也只会丢失一个事件循环所产生的命令数据
# appendfsync always
# everysec 服务器在每个事件循环都要将aof_buf缓冲区所有内容写入到AOF文件,并且每隔一秒就要在子线程中对AOF进行一次同步。效率比较快
appendfsync everysec
# no 服务器在每个事件循环都要将aof_buf缓冲区所有内容写入到AOF文件,至于何时对AOF进行同步,由操作系统控制,写入速度最快。但是由于积累数据较多,导致同步时间最长,停机时会导致上次同步的AOF文件后的数据丢失
# appendfsync no
加入缓冲区是为提高文件写入的效率,但是同样带来了安全问题,缓冲区的数据毕竟还加载在内存中,如果此时停服务会使得缓存区中的数据丢失。系统服务提供了函数fsync和fdata_sync可以强制将缓冲区的数据立即写入到磁盘中,这也就是AOF持久化是实现同步和写入的原理。
AOF重写:
实现重写的原因:
AOF文件存储的操作数据的命令,而且随着时间的推移操作的命令会来越来越多,文件体积也会越来越大,占用磁盘空间越大,使用AOF文件数据还原的时间也会越来越长。所有Redis提供了AOF重写功能,通过创建一个新的AOF文件提换原来的AOF文件。新文件中不需要分析、读取就的AOF文件内容,新旧文件保存的数据状态相同,新AOF文件不会包含任何浪费空间的冗余命令。例如:存储一个list分6次存处元素,但是新的AOF文件只会保留RPUSH多个元素的命令,这样就大大节省AOF文件的体积。
执行:
AOF重写是fork出一个子进程来进行执行,将数据方入到AOF的重写缓冲区不影响主进程的处理请求,进程操作在无锁状态下保证了数据的安全性。
手动执行:
BGREWRITEAOF
自动执行配置:
############################## APPEND ONLY MODE ###############################
#当前的AOF文件大小超过上一次重写时AOF文件大小的百分比会进行重写,如果之前没有重写过,则以启动文件启动大小为依据
auto-aof-rewrite-percentage 100
#允许重写的最小AOF文件大小
auto-aof-rewrite-min-size 64mb
AOF文件示例:
如何选择两种持久化机制?
RDB非常适合做备份,可以分时间段进行备份,如果允许丢失短时间的数据可以只使用RDB机制,而RDB的缺点也就是因为时效问题,如果因为数据库故障时,那么在上个备份版本之间的数据都会丢失;
而AOF使得数据丢失已经降到很低,everysec会每秒执行一次fsync 策略,及时数据库故障,也只会丢失1秒的数据,AOF的缺点是文件体积 比RDB要大,速度会比RDB慢,同时执行备份的次数也要比RDB多,备份时会产生大量的IO操作,影响Redis的效率。
Redis 支持同时开启 RDB 和 AOF,系统重启后,Redis 会优先使用 AOF 来恢复数据,这样丢失的数据会最少。不过Redis4.0以后的版本开始支持 RDB 和 AOF 的混合持久化(默认关闭的,通过配置项 aof-use-rdb-preamble
开启)。如果把混合持久化打开,AOF 重写的时候就直接把 RDB 的内容写到 AOF 文件开头。这样做的好处是可以结合 RDB 和 AOF 的优点, 快速加载同时避免丢失过多的数据。当然缺点就是 AOF 里面的 RDB 部分是压缩格式不再是 AOF 格式,可读性变差。
=================================================================
Redis版本:3.0.7
参考书籍《Redis设计与实现》 黄健宏 著