Redis(四)-日志

概述

本节主要分析下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日志的目的是为了给数据做持久化,以便宕机或重启时还原内存数据,要实现这个目标需要考虑几个问题:

  1. 文件系统本身对文件大小有限制,无法保存过大的文件;
  2. 如果文件太大,之后再往里面追加命令记录的话,效率也会变低;
  3. 如果发生宕机,AOF中记录的命令要一个个被重新执行,用于故障恢复,如果日志文件太大,整个恢复过程就会非常缓慢,这就会影响到Redis的正常使用
  4. 写AOF日志会影响主线程响应用户请求

1.4 reids如何解决?

  1. 采用重写机制:首先从数据库中读取键现在的值,然后用一条命令去记录键值对,代替之前记录该键值对的多个命令;
  2. 主线程fork一个后台子线程bgrewriteaof来进行重写,避免阻塞主线程;
  3. Redis增加了一个AOF重写缓存区用于记录重写过程中主进程接收到的命令;
    Redis(四)-日志_第1张图片
    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还存在什么问题?

  1. AOF日志虽然提供了三种写策略,但是无法同时实现即高效又不丢失数据;
  2. AOF日志记录的是命令,数据恢复时需要一条条重新执行,这个效率较低;

要想解决这两个问题就需要引入下面的RDB,gogogo...

2. RDB

2.1 是什么?

RDB即内存快照,就是指内存中的数据在某一个时刻的状态记录(类似thread dump),把这一时刻的状态以文件的形式写到磁盘文件上,用于数据恢复;

2.2 怎么做?

  1. 为了避免阻塞主线程,redis采用通过bgsave来fork子进程进行操作;
  2. 为了在快照时允许写请求继续执行,redis利用Linux写时复制技术;
    Redis(四)-日志_第2张图片
    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的方案,再继续思考下要想实现即高效又完全不丢失数据的目标,还存在哪些问题:

  1. 频繁写RDB,将全量数据写入磁盘,会给磁盘带来很大压力,多个快照竞争有限的磁盘带宽时,前一个快照还没有做完,后一个又开始做了,容易造成恶性循环
  2. bgsave子进程需要通过fork操作从主进程创建出来,过程中需要复制主进程必要的数据结构,内存越大虚拟内存到物理内存映射索引表越大,fork耗时越大,主进程阻塞时间越长
  3. 如果调低RDB执行频率,就会失去做快照的意义;
  4. 当单实例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 架构

  1. 整体架构


    Redis(四)-日志_第3张图片
    Pika.png
  • 网络模块:主要负责底层网络请求的接收和发送
  • 线程模块:采用了多线程模型来具体处理客户端请求,包括一个请求分发线程、一组工作线程以及一个线程池
  • Nemo模块:对Redis的数据类型做适配,使Pika兼容redis数据类型
  • binlog模块:记录写命令,用于主从节点的命令同步
  • RocksDB模块:持久化键值数据库,存储数据;为了把数据保存到SSD,Pika使用了业界广泛应用的持久化键值数据库RocksDB
  1. RocksDB写入流程


    Redis(四)-日志_第4张图片
    Pika-RocksDB.png
  • RocksDB使用两小块内存空间(Memtable1和Memtable2)来交替缓存写入的数据
  • 当有数据要写入RocksDB时,RocksDB会先把数据写入到Memtable1。等到Memtable1写满后,再把数据以文件的形式快速写入底层的SSD;同时,RocksDB会使用Memtable2来代替Memtable1,缓存新写入的数据

3.3 Pika优点

  1. 使用了SSD来保存数据,解决了Redis使用内存成本太高的问题
  2. 全量同步时直接读取RocksDB的数据文件,增量同步时使用binlog机制,不使用RDB内存快照,避免了大内存时的影响
  3. Pika实例重启时,可以直接从SSD上的数据文件中读取数据,不用像RDB文件全部重新加载数据或是从AOF文件中全部回放操作,重启快
  4. Pika通过binlog机制实现写命令的增量同步,不再受内存缓冲区大小的限制,不用担心缓冲区溢出而触发的主从库重新全量同步问题

总结

本节分析了AOF、RDB、Pika三种缓冲方案的实现,以及各自解决了什么问题,又带来了什么问题;具体使用时还要具体分析权衡利弊,下面几点建议

  • 数据不能丢失时,内存快照和AOF的混合使用是一个很好的选择;
  • 如果允许分钟级别的数据丢失,可以只使用RDB;
  • 如果只用AOF,优先使用everysec的配置选项,因为它在可靠性和性能之间取了一个平衡
    --------over--------

你可能感兴趣的:(Redis(四)-日志)