1. RDB的实现
RDB文件用于保存和还原Redis服务器所有数据中的所有键值对数据。
SAVE命令由服务器进程直接执行保存操作,所以该命令会阻塞服务器。
BGSAVE命令由子进程执行保存操作,该命令不会阻塞服务器。
RDB文件的加载工作是在服务器启动时自动执行的,Redis并没有提供专门用于加载RDB文件的命令。
服务器状态中保存所有用save选项设置的保存条件,当任意一个保存条件被满足时,服务器会自动执行BGSAVE命令。
Redis还可以通过配置文件的选项来实现每隔一段时间自动执行一次bgsave命令,默认会提供以下配置:
1
2
3
4
5
6
7
8save 900 1
save 300 10
save 60 10000
上面条件只要满足任意一个,就会执行bgsave,它们的意思分别是:
900s内,对数据库进行了至少一次修改;
300s内,对数据库进行了至少10次修改;
60s内,对数据库进行了至少10000次修改;执行快照时,数据能被修改吗?
Redis可以继续处理命令,也就是数据能被修改的。关键技术在于写时复制技术。
执行bgsave命令的时候,会通过fork()创建子进程,此时子进程和父进程是共享同一片内存数据的,因为创建子进程的时候,会复制父进程的页表,但是页面指向的是物理内存还是一个。
只有在发生修改内存数据的情况下,物理内存才会被复制一份。
这样的目的是为了减少创建子进程时的性能损耗,从而加快创建子进程的速度,毕竟创建子进程的过程中,是会阻塞主线程的。
所以创建bgsave子进程后,由于共享父进程的所有内存数据,于是可以直接读取主进程里的内存数据,并将数据写入到RDB文件。
当主进程对这些共享的内存数据也都是只是读操作,那么,主线程和bgsave子进程相互不影响。
但是如果主线程要修改共享数据里的某一块时,就会发生写时复制,于是这块数据的物理内存就会被复制一份,然后主线程在这个数据副本进行修改操作。与此同时bgsave子进程可以继续把原来的数据写入到RDB文件。
bgsave快照过程中,如果主线程修改了共享数据,发生了写时复制后,RDB快照保存的是原来笨的内存数据,而主线程刚修改的数据,是没办法在这一时间写入到RDB文件的,只能交由下一次的bgsave快照。
在极端场景下,如果所有的共享内存都被修改,则此时的内存占用是原先的2倍。
RDB文件时一个经过压缩的二进制文件,由多个部分组成。
对于不同类型的键值对,RDB会使用不同的方式来保存它们。
2. AOF的实现
AOF文件通过保存所有修改数据库的写命令请求来记录服务器的数据库状态。
AOF文件中的所有命令都以Redis命令请求协议的格式保存;
命令请求会先保存到AOF缓存区里面,之后再定期写入并同步到AOF文件。
- Redis执行完写操作命令后,会将命令追加到server.aof_buf缓存区;
- 然后通过write()系统调用,将aof_buf缓存区的数据写入到aof文件,此时数据并没有写入到硬盘,而是拷贝到了内核缓存区page cache,等待内核将数据写入硬盘。
- 具体内核缓存区的数据什么时间会写入到硬盘,由内核决定。
appendfsync选项的不同值对AOF持久化功能的安全性以及Redis服务器的性能由很大影响。
Redis规定了三种写会硬盘的策略,由redis.conf配置文件中的appendfsync配置项的三种参数可填:
- Always:可以最大程度的保证数据不丢失,但是由于它每执行一条写操作民工就同步将AOF内容写回硬盘,所以是不可以避免会影响主进程的性能。
- Everysec:折中方案;
- No:交由操作系统来决定何时将AOF日志内容写回硬盘,相较于Always策略性能较好,但是操作系统写回硬盘的时间是不可预知的,如果AOF内容也没有写回硬盘,一旦服务器宕机,就会丢失不定数量的数据。
这2种写会策略都无法完美解决【主进程阻塞】和【减少数据丢失】的问题。因为两个问题是对立的,偏向于一边,就会牺牲另外一边。
三种写回策略的优缺点:
服务器只要载入并重新执行保存在AOF文件中的命令,就可以还原数据库本来的状态。
AOF重写可以产生一个新的AOF文件,这个新的AOF文件和原有的AOF文件所保存的数据库状态一样,但是提及更小。
AOF重写是一个有歧义的名字,原理是通过度武器数据库中的键值对来实现的,程序无需对现有的AOF文件进行任何读取、分析或者写入操作。
在执行BGREWRITEAOF命令时,Redis服务器会维护一个AOF重写缓存区,该缓存区会在子进程创建新的AOF文件期间,记录服务器执行的所有写命令,当子进程完成创建新的AOF文件的工作之后,服务器会将重写缓存区中的所有内容追加到新的AOF文件的末尾,是的新旧两个AOF文件所保存的数据库状态一致,最后,服务器用新的AOF文件替换旧的AOF文件,一次来完成AOF文件重写操作。
3. Redis4.0混合持久化
重启 Redis 时,我们很少使用 rdb 来恢复内存状态,因为会丢失大量数据。我们通使用 AOF 日志重放,但是重放 AOF 日志性能相对 rdb 来说要慢很多,这样在 Redis 实例很大的情况下,启动需要花费很长的时间。
Redis 4.0 为了解决这个问题,带来了一个新的持久化选项——混合持久化。将 rdb 文件的内容和增量的 AOF 日志文件存在一起。这里的 AOF 日志不再是全量的日志,而是自持久化开始到持久化结束的这段时间发生的增量 AOF 日志,通常这部分 AOF 日志很小。
于是在 Redis 重启的时候,可以先加载 rdb 的内容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,重启效率因此大幅得到提升。
4. RDB、AOF和复制对过期键的处理
4.1 生成RDB文件
在执行SAVE命令或者检查BGSAVE命令创建一个新的RDB文件时,程序会对数据库中的键进行检查,已过期的键不会被保存到新创建的RDB文件。
4.2 载入RDB文件
在启动redis时,如果服务器开启了RDB功能,那么服务器将对RDB文件进行载入:
- 如果服务器以主服务器模式运行,那么在载入RDB文件时,程序会对文件中保存的键进行检查,未过期的键会被载入到数据库中,而过期键则会被忽略,所以过期键对载入RDB文件的主服务器不会造成影响。
- 如果服务器以从服务器模式运行,那么在载入RDB文件时,文件中保存的所有的键,不论是否过期,都会被载入到数据库中。不过,因为主从服务器在进行数据同步的时候,从服务器的数据库就会被清空,所以一般来讲,过期键对载入RDB文件的从服务器也不会造成影响。
4.3 AOF文件写入
当服务器以AOF持久化模式运行时,如果数据库中的某个键已经过期,但它还没有被惰性删除或者定期删除,那么AOF文件不会对这个过期键产生任何影响。
当过期键被惰性删除或者定期删除之后,程序会向AOF文件追加delete命令来显式地记录该键已经被删除。
4.4 AOF重写
和生成RDB文件类似,在执行AOF重写过程中,程序会对数据库中的键进行检查,在过期的键不会被保存到重写后的AOF文件中。
4.5 复制
当服务器运行在复制模式下时,从服务器的过期键删除动作由主服务器控制。
- 主服务器在删除一个过期键之后,会显式地向所有从服务器发送一个del命令,告知从服务器删除这个过期键。
- 从服务器在执行客户端发送的读命令时,即使遇到过期键也不会将过期键删除,而是继续像处理未过期的键一样来处理过期键。
- 从服务器只有在接到主服务器发来的del命令之后,才会删除过期键。