Redis原理探究(一)
Redis原理探究(一)
Redis持久化
Redis是内存数据库,宕机后数据会消失。 Redis重启后快速恢复数据,要提供持久化机制 Redis持久化是为了快速的恢复数据而不是为了存储数据 Redis有两种持久化方式:RDB和AOF
注意:Redis持久化不保证数据的完整性。 当Redis用作DB时,DB数据要完整,所以一定要有一个完整的数据源(文件、mysql) 在系统启动时,从这个完整的数据源中将数据load到Redis中 数据量较小,不易改变,比如:字典库(xml、Table) 通过info命令可以查看关于持久化的信息
持久化方式一:RDB
RDB(Redis DataBase),是redis默认的存储方式,RDB方式是通过快照( snapshotting )完成 的。
触发快照的方式
-
符合自定义配置的快照规则
-
在redis中的redis.conf配置文件中
save "" # 不使用RDB存储 不能主从 save 900 1 # 表示15分钟(900秒钟)内至少1个键被更改则进行快照。 save 300 10 # 表示5分钟(300秒)内至少10个键被更改则进行快照。 save 60 10000 # 表示1分钟内至少10000个键被更改则进行快照。
-
-
执行save或者bgsave命令
-
执行flushall命令
-
执行主从复制操作 (第一次)
RDB执行流程(原理)
- Redis父进程首先判断:当前是否在执行save,或bgsave/bgrewriteaof(aof文件重写命令)的子 进程,如果在执行则bgsave命令直接返回
- 父进程执行fork(调用OS函数复制主进程)操作创建子进程,这个复制过程中父进程是阻塞的, Redis不能执行来自客户端的任何命令。
- 父进程fork后,bgsave命令返回”Background saving started”信息并不再阻塞父进程,并可以响 应其他命令。
- 子进程创建RDB文件,根据父进程内存快照生成临时快照文件,完成后对原有文件进行原子替换。 (RDB始终完整)
- 子进程发送信号给父进程表示完成,父进程更新统计信息。
- 父进程fork子进程后,继续工作。
RDB文件结构
-
头部5字节固定为“REDIS”字符串
-
4字节“RDB”版本号(不是Redis版本号),当前为9,填充后为0009
-
辅助字段,以key-value的形式
-
存储数据库号码
-
字典大小
-
过期key
-
主要数据,以key-value的形式存储
-
结束标志
-
校验和,就是看文件是否损坏,或者是否被修改。
RDB的优缺点
优点
RDB是二进制压缩文件,占用空间小,便于传输(传给slaver)
主进程fork子进程,可以最大化Redis性能,主进程不能太大,Redis的数据量不能太大,复制过程中主
进程阻塞
缺点
不保证数据完整性,会丢失最后一次快照以后更改的所有数据
持久化方式二:AOF
AOF(append only file)是Redis的另一种持久化方式。Redis默认情况下是不开启的。开启AOF持久化后Redis 将所有对数据库进行过写入的命令(及其参数)(RESP)记录到 AOF 文件, 以此达到记录数据 库状态的目的,这样当Redis重启后只要按顺序回放这些命令就会恢复到原始状态了
AOF会记录过程,RDB只管结果
AOF持久化实现
redis.conf
# 可以通过修改redis.conf配置文件中的appendonly参数开启
appendonly yes
# AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的。
dir ./
# 默认的文件名是appendonly.aof,可以通过appendfilename参数修改
appendfilename appendonly.aof
AOF原理
AOF文件中存储的是redis的命令,同步命令到 AOF 文件的整个过程可以分为三个阶段:
-
命令传播:Redis 将执行完的命令、命令的参数、命令的参数个数等信息发送到 AOF 程序中。
-
缓存追加:AOF 程序根据接收到的命令数据,将命令转换为网络通讯协议的格式,然后将协议内容追加 到服务器的 AOF 缓存中。
-
文件写入和保存:AOF 缓存中的内容被写入到 AOF 文件末尾,如果设定的 AOF 保存条件被满足的话, fsync 函数或者 fdatasync 函数会被调用,将写入的内容真正地保存到磁盘中。
缓存追加
当命令被传播到 AOF 程序之后, 程序会根据命令以及命令的参数, 将命令从字符串对象转换回原来的 协议文本。协议文本生成之后, 它会被追加到 redis.h/redisServer 结构的 aof_buf 末尾。
redisServer 结构维持着 Redis 服务器的状态, aof_buf 域则保存着所有等待写入到 AOF 文件的协 议文本(RESP)。
文件写入和保存
每当服务器常规任务函数被执行、 或者事件处理器被执行时, aof.c/flushAppendOnlyFile 函数都会被 调用, 这个函数执行以下两个工作:
-
WRITE:根据条件,将 aof_buf 中的缓存写入到 AOF 文件。
-
SAVE:根据条件,调用 fsync 或 fdatasync 函数,将 AOF 文件保存到磁盘中。
AOF 保存模式
Redis 目前支持三种 AOF 保存模式,它们分别是:
- AOF_FSYNC_NO :不保存:每次调用 flushAppendOnlyFile 函数, WRITE 都会被执行, 但 SAVE 会被略过,在这种模式下, SAVE 只会在以下任意一种情况中被执行:
- Redis 被关闭
- AOF 功能被关闭
- 系统的写缓存被刷新(可能是缓存已经被写满,或者定期保存操作被执行)
- 这三种情况下的 SAVE 操作都会引起 Redis 主进程阻塞。
- AOF_FSYNC_EVERYSEC :每一秒钟保存一次。(默认) :SAVE 操作是由后台子线程(fork)调用 的, 所以它不会引起服务器主进程阻塞
- AOF_FSYNC_ALWAYS :每执行一个命令保存一次。(不推荐):SAVE 是由 Redis 主进程执行的,所以在 SAVE 执行期间,主进程会被阻塞
AOF重写、触发方式、混合持久化
AOF记录数据的变化过程,越来越大,需要重写“瘦身”
Redis可以在 AOF体积变得过大时,自动地在后台(Fork子进程)对 AOF进行重写。重写后的新AOF文件包含了恢复当前数据集所需的最小命令集合,重写:对数据库中键的当前值进行优化
Redis不希望AOF重写造成服务器无法处理请求, 所以Redis决定将AOF重写程序放到(后台)子进程里执行, 这样处理的最大好处是:
- 子进程进行 AOF 重写期间,主进程可以继续处理命令请求
- 子进程带有主进程的数据副本,使用子进程而不是线程,可以在避免锁的情况下,保证数据的安全性。
不过, 使用子进程也有一个问题需要解决: 因为子进程在进行 AOF 重写期间, 主进程还需要继续处理命令, 而新的命令可能对现有的数据进行修改, 这会让当前数据库的数据和重写后的 AOF文件中的数据不一致。
为了解决这个问题, Redis增加了一个AOF重写缓存, 这个缓存在fork出子进程之后开始启用, Redis 主进程在接到新的写命令之后, 除了会将这个写命令的协议内容追加到现有的 AOF 文件之外, 还会追加到这个缓存中。
重写过程分析
整个重写操作是绝对安全的
Redis在创建新AOF文件的过程中,会继续将命令追加到现有的AOF文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。 而一旦新AOF文件创建完毕,Redis 就会从旧 AOF 文件切换到 新 AOF 文件,并开始对新 AOF 文件进行追加操作。
当子进程在执行 AOF 重写时, 主进程需要执行以下三个工作:
- 处理命令请求。
- 将写命令追加到现有的 AOF 文件中。
- 将写命令追加到 AOF 重写缓存中。
优势:现有的 AOF 功能会继续执行,即使在 AOF 重写期间发生停机,也不会有任何数据丢失。 所有对数据库进行修改的命令都会被记录到 AOF 重写缓存中。
当子进程完成 AOF 重写之后, 它会向父进程发送一个完成信号, 父进程在接到完成信号之后, 会调用 一个信号处理函数, 并完成以下工作:
- 将 AOF 重写缓存中的内容全部写入到新 AOF 文件中。
- 对新的 AOF 文件进行改名,覆盖原有的 AOF 文件。
这个信号处理函数执行完毕之后,主进程就可以继续像往常一样接受命令请求了。 在整个AOF后台重写过程中, 只有最后的写入缓存和改名操作会造成主进程阻塞,在其他时候,AOF后台重写都不会对主进程造成阻塞, 这将 AOF 重写对性能造成的影响降到了最低。
触发方式
-
配置触发
# 表示当前aof文件大小超过上一次aof文件大小的百分之多少的时候会进行重写。如果之前没有重写过, 以启动时aof文件大小为准 auto-aof-rewrite-percentage 100 # 限制允许重写最小aof文件大小,也就是文件大小小于64mb的时候,不需要进行优化 auto-aof-rewrite-min-size 64mb
-
执行bgrewriteaof命令
127.0.0.1:6379> bgrewriteaof Background append only file rewriting started
混合持久化
RDB和AOF各有优缺点,Redis 4.0 开始支持 rdb 和 aof 的混合持久化。如果把混合持久化打开,aof rewrite 的时候就直接把 rdb 的内容写到 aof 文件开头。
RDB的头+AOF的身体---->appendonly.aof
开启混合持久化
aof-use-rdb-preamble yes
查看AOF文件是rdb文件的头和aof格式的内容,在加载时,首先会识别AOF文件是否以 REDIS字符串开头,如果是就按RDB格式加载,加载完RDB后继续按AOF格式加载剩余部分。
AOF文件的载入与数据还原
因为AOF文件里面包含了重建数据库状态所需的所有写命令,所以服务器只要读入并重新执行一遍AOF 文件里面保存的写命令,就可以还原服务器关闭之前的数据库状态 Redis读取AOF文件并还原数据库状态的详细步骤如下:
-
创建一个不带网络连接的伪客户端(fake client):因为Redis的命令只能在客户端上下文中执行, 而载入AOF文件时所使用的命令直接来源于AOF文件而不是网络连接,所以服务器使用了一个没有网络连接的伪客户端来执行AOF文件保存的写命令,伪客户端执行命令的效果和带网络连接的客户端执行命令的效果完全一样
-
从AOF文件中分析并读取出一条写命令
-
使用伪客户端执行被读出的写命令
-
一直执行步骤2和步骤3,直到AOF文件中的所有写命令都被处理完毕为止 当完成以上步骤之后,AOF文件所保存的数据库状态就会被完整地还原出来
整个过程如下图所示:
RDB与AOF对比
-
RDB存某个时刻的数据快照,采用二进制压缩存储,AOF存操作命令,采用文本存储(混合)
-
RDB性能高、AOF性能较低
-
RDB在配置触发状态会丢失最后一次快照以后更改的所有数据,AOF设置为每秒保存一次,则最多丢2秒的数据
-
Redis以主服务器模式运行,RDB不会保存过期键值对数据,Redis以从服务器模式运行,RDB会保 存过期键值对,当主服务器向从服务器同步时,再清空过期键值对。
AOF写入文件时,对过期的key会追加一条del命令,当执行AOF重写时,会忽略过期key和del命令。
应用场景
内存数据库选择rdb+aof 数据不容易丢
有原始数据源: 每次启动时都从原始数据源中初始化 ,则 不用开启持久化 (数据量较小)
缓存服务器 rdb 一般 性能高
在数据还原时:
有rdb+aof 则还原aof,因为RDB会造成文件的丢失,AOF相对数据要完整。
只有rdb,则还原rdb