概述
本节主要分析下Redis日志持久化机制,包括RDB、AOF以及360开源的Pika
1. AOF
1.1 是什么?
AOF是写后日志
,即先写内存再记录日志;日志中记录用户的操作命令(类似mysql的binlog)
-
优点
:不需要检查命令是否正确(如果命令有问题的话写内存时就会报错);不会阻塞当前的写操作 -
缺点
:写内存之后记录日志之前宕机,会导致数据丢失;是在主线程中执行的,可能会阻塞后续写操作
举例:127.0.0.1:6379> set testkey testvalue
--AOF日志:
*3
$3
set
$7
testkey
$9
testvalue
-
*3
:表示当前命令有三个部分,每部分都是由“$+数字”开头,后面紧跟着具体的命令、键或值 -
数字
:表示这部分中的命令、键或值一共有多少字节 -
$3 set
:就表示这部分有3个字节,也就是“set”命令
1.2 写策略
由于Redis是单线程,如果主线程处理写AOF务必会影响用户请求,因此Redis提供了三种写策略
-
Always(同步写回)
:每个写命令执行完,立马同步地将日志写回磁盘; -
Everysec(每秒写回)
:每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区,每隔一秒把缓冲区中的内容写入磁盘; -
No(操作系统控制写回)
:每个写命令执行完,只是先把日志写到AOF文件的内存缓冲区,由操作系统决定何时将缓冲区内容写回磁盘
小结:
Always
可靠性高,数据基本不丢失,但是每个命令都要写磁盘,性能影响较大;
Everysec
性能适中,宕机时最多丢失1秒数据,Redis的默认策略
No
性能好,但是宕机时丢失数据较多
1.3 存在的问题
思考此时AOF日志机制存在什么问题?
写AOF日志的目的是为了给数据做持久化,以便宕机或重启时还原内存数据,要实现这个目标需要考虑几个问题:
- 文件系统本身对文件大小有限制,无法保存过大的文件;
- 如果文件太大,之后再往里面追加命令记录的话,效率也会变低;
- 如果发生宕机,AOF中记录的命令要一个个被重新执行,用于故障恢复,如果日志文件太大,整个恢复过程就会非常缓慢,这就会影响到Redis的正常使用
- 写AOF日志会影响主线程响应用户请求
1.4 reids如何解决?
- 采用重写机制:首先从数据库中读取键现在的值,然后用一条命令去记录键值对,代替之前记录该键值对的多个命令;
- 主线程fork一个后台子线程bgrewriteaof来进行重写,避免阻塞主线程;
- Redis增加了一个AOF重写缓存区用于记录重写过程中主进程接收到的命令;
redis-AOF重写.png
重写步骤解析:
1.
:主进程接收客户端请求命令
2.
:触发AOF重写时,主进程fork一个bgrewriteaof子进程,该子进程指向与父进程相同的内存地址空间
a. 子进程开始
:AOF重写,把内存中的所有数据写入到新的AOF文件中,该过程可能耗时较长;但是主进程可以继续处理请求;
3.
:操作写入redis
4.
:日志写入AOF缓冲区
5.
:日志写入AOF重写缓冲区
6.
:AOF缓冲区日志继续写入原AOF文件
-----分割线 子进程重写完之后向父进程发送完成信号,父进程继续处理-----
7.
:主进程将AOF重写缓冲区中的内容全部写入到新的AOF文件中;这个时候新的AOF文件所保存的数据库状态和服务器当前的数据库状态一致
8.
:对新的AOF文件进行改名,原子的覆盖原有的AOF文件;完成新旧两个AOF文件的替换
1.5 触发重写条件
触发写AOF有两种方式:
-
手动触发
:用户通过调用BGREWRITEAOF
手动触发 -
自动触发
:每次当服务器周期性操作函数(serverCron)执行时,会检查aof文件体量是否超过64mb、是否比上次重写后的体量增加了100%,两者都满足时触发重写
配置参数:
AOF增加百分比:auto-aof-rewrite-percentage默认100
AOF重写阈值: auto-aof-rewrite-min-size默认64mb
1.6 重写后还存在什么问题?
再来思考下:重写机制之后AOF日志用于重启或宕机恢复redis还存在什么问题?
- AOF日志虽然提供了三种写策略,但是无法同时实现即高效又不丢失数据;
- AOF日志记录的是命令,数据恢复时需要一条条重新执行,这个效率较低;
要想解决这两个问题就需要引入下面的RDB,gogogo...
2. RDB
2.1 是什么?
RDB即内存快照,就是指内存中的数据在某一个时刻的状态记录(类似thread dump),把这一时刻的状态以文件的形式写到磁盘文件上,用于数据恢复;
2.2 怎么做?
- 为了避免阻塞主线程,redis采用通过bgsave来fork子进程进行操作;
- 为了在快照时允许写请求继续执行,redis利用Linux写时复制技术;
redis-RDB.png
写RDB步骤解析:
1.
:主进程接收客户端请求;
2.
:触发bgsave时,主进程fork出bgsave子进程,fork时会复制主进程页表,子进程就可以不用复制物理内存而直接访问主进程内存;
a. 子进程
:把内存快照数据写入RDB文件
3.
:读操作主进程通过页表找到物理页直接进行读取,不影响bgsave进程写RDB文件
4.
:写操作,假如主线程需要修改虚页7里的数据,那么,主线程就需要新分配一个物理页(假设是物理页55),然后把修改后的虚页7里的数据写到物理页55上,而虚页7里原来的数据仍然保存在物理页33上。这个时候,虚页7到物理页33的映射关系,仍然保留在bgsave子进程中。所以,bgsave子进程可以无误地把虚页7的原始数据写入RDB文件
5.
:bgsave进程写完通知主进程,主进程把临时RDB文件替换并删除原RDB文件
2.3 触发条件
触发RDB快照跟AOF一样,同样有两种方式:
-
手动触发
:用户通过执行save 或 bgsave
命令触发 -
自动触发
:在Redis配置文件中的save指定多少秒内至少发生多少次写操作
就触发
--redis默认
# 900s内至少达到一条写命令
save 900 1
# 300s内至少达至10条写命令
save 300 10
# 60s内至少达到10000条写命令
save 60 10000
2.4 啥问题?
看完AOF和RDB的方案,再继续思考下要想实现即高效又完全不丢失数据的目标,还存在哪些问题:
- 频繁写RDB,将全量数据写入磁盘,会给磁盘带来很大压力,多个快照竞争有限的磁盘带宽时,前一个快照还没有做完,后一个又开始做了,容易造成恶性循环
- bgsave子进程需要通过fork操作从主进程创建出来,过程中需要复制主进程必要的数据结构,内存越大虚拟内存到物理内存映射索引表越大,fork耗时越大,主进程阻塞时间越长
- 如果调低RDB执行频率,就会失去做快照的意义;
- 当单实例redis内存非常大时,也会存在一些问题:
- RDB文件生成时的fork时长就会增加,会导致Redis实例阻塞
- RDB进行恢复的时长也会增加,会导致Redis较长时间无法对外提供服务
- 主从同步时,会导致全量同步的时长增加,效率不高,主从切换慢
2.5 如何解决?
- Redis4.0以后混合使用AOF日志和内存快照,内存快照以一定的频率执行,在两次快照之间,使用AOF日志记录这期间的所有命令操作(rdb.dump中前段是rdb格式,后段是aof格式),这样既能享受到RDB文件快速恢复的好处,又能享受到AOF只记录操作命令的简单优势。
- 混用模式可以解决2.4中123的问题,但是仍然无法解决4单实例内存过大的问题,通常可能会想到使用Redis切片集群,把数据分散保存到多个实例上。但是这样做的话,如果要保存的数据总量很大,但是每个实例保存的数据量较小的话,就会导致集群的实例规模增加,这会让集群的运维管理变得复杂,增加开销
- 下面看下360公司开源的Pika键值数据库如何解决这些问题
3. Pika
3.1 是什么?
Pika 主要解决的是用户使用 Redis 的内存大小超过 50G、80G 等等这样的情况,会遇到启动恢复时间长,一主多从代价大,硬件成本贵,缓冲区容易写满等问题。Pika 就是针对这些场景的一个解决方案。
3.2 架构
-
整体架构
Pika.png
-
网络模块
:主要负责底层网络请求的接收和发送 -
线程模块
:采用了多线程模型来具体处理客户端请求,包括一个请求分发线程、一组工作线程以及一个线程池 -
Nemo模块
:对Redis的数据类型做适配,使Pika兼容redis数据类型 -
binlog模块
:记录写命令,用于主从节点的命令同步 -
RocksDB模块
:持久化键值数据库,存储数据;为了把数据保存到SSD,Pika使用了业界广泛应用的持久化键值数据库RocksDB
-
RocksDB写入流程
Pika-RocksDB.png
- RocksDB使用两小块内存空间(Memtable1和Memtable2)来交替缓存写入的数据
- 当有数据要写入RocksDB时,RocksDB会先把数据写入到Memtable1。等到Memtable1写满后,再把数据以文件的形式快速写入底层的SSD;同时,RocksDB会使用Memtable2来代替Memtable1,缓存新写入的数据
3.3 Pika优点
- 使用了SSD来保存数据,解决了Redis使用内存成本太高的问题
- 全量同步时直接读取RocksDB的数据文件,增量同步时使用binlog机制,不使用RDB内存快照,避免了大内存时的影响
- Pika实例重启时,可以直接从SSD上的数据文件中读取数据,不用像RDB文件全部重新加载数据或是从AOF文件中全部回放操作,重启快
- Pika通过binlog机制实现写命令的增量同步,不再受内存缓冲区大小的限制,不用担心缓冲区溢出而触发的主从库重新全量同步问题
总结
本节分析了AOF、RDB、Pika三种缓冲方案的实现,以及各自解决了什么问题,又带来了什么问题;具体使用时还要具体分析权衡利弊,下面几点建议
- 数据不能丢失时,内存快照和AOF的混合使用是一个很好的选择;
- 如果允许分钟级别的数据丢失,可以只使用RDB;
- 如果只用AOF,优先使用everysec的配置选项,因为它在可靠性和性能之间取了一个平衡
--------over--------
网友评论