前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >redis学习笔记(二)AOF日志

redis学习笔记(二)AOF日志

作者头像
虞大大
发布2020-12-15 10:38:46
4960
发布2020-12-15 10:38:46
举报
文章被收录于专栏:码云大作战码云大作战

对于正常的redis使用,如果redis中存放了很重要的数据,并且一旦redis数据丢失的情况下,就需要重新恢复数据。一般情况最容易解决的方法是:从数据库中读取数据再set进缓存中。但是这样的解决方式也有很大的弊端:比如对于数据库:需要频繁访问数据库,会给数据库带来很大的压力。

对于redis来说,也提供了两种持久化机制,来保证我们从日志中来恢复redis数据。

reids的持久化机制主要是AOF日志和RDB快照日志。这篇文章主要学习下AOF日志。

一、AOF日志是如何实现的?

先看下AOF日志中存放的内容。

如上图所示,我们这条命令是set key value,这条命令就会分成以下几个部分被保存在aof文件中。

(1)*3,表示这条命令有三个部分。

(2)$3 set ,表示第一个命令是set,并且占了3个字节。

(3)$3 key,表示key是key,并且占了3个字节。

(4)$5 value,表示value是value,并且占了5个字节。

一条命令就会被分割成以上几个部分,存放在aof文件中。为了避免检查aof日志中的命令而导致redis命令报错,一般情况下aof日志记录命令都是在redis命令执行之后放入的。这样操作的好处:

(1)避免了命令执行中额外的检查开销。

(2)先执行命令的好处在于只有命令执行成功才会被记录到日志中,避免出现aof日志中存在命令,而redis本身操作这条命令失败的情况。

(3)先执行命令,保证了不会在aof日志中记录错误的命令,防止在使用aof日志恢复数据时出错的情况。因为redis先执行命令,如果命令有问题就直接返回操作失败给客户端,并不会操作aof日志。

这样操作的风险:

(1)如果刚执行完一个命令,立马就宕机了,会导致这条命令没有被记录到aof日志中,就会丢失了这条命令。所以如果将redis功能作为数据库来使用的话会有比较严重的数据丢失问题。

(2)如果写命令很多,会导致aof日志写入磁盘时的压力变大。

二、AOF的三种写入策略

为了解决上述风险中的第二个问题,aof提供了三种写入策略,可在aof配置项的appendfsync中选择。

(1)Always - 同步写回

每条写命令执行完毕,立马同步的将日志同步到磁盘。

(2)Eversec - 每秒写回

每个写命令执行完毕,只是先把日志写到aof文件的内存缓冲区。每隔一秒把缓冲区中的内容写入磁盘。

(3)No - 操作系统控制写回

每个写命令执行完毕,只是先把日志写到aof文件的内存缓冲区。操作系统自行决定何时将缓冲区内容写入到磁盘。

三种选择的性能比较:

(1)Always - 同步写回

同步写回虽然可以做到数据的不丢失,但是在每一个写命令之后都有一个同步的写入磁盘操作,会影响redis主线程性能。

(2)Eversec - 每秒写回

每秒写回策略,在写命令时,只是将写命令的aof写到缓冲区就可返回,不会影响redis主线程性能。缺点是,写入磁盘的时机不由redis主线程把控,一旦aof记录没有写到磁盘,发生宕机,对应的数据就会丢失。

(3)No - 操作系统控制写回

操作系统写回的策略与每秒写回类似,区别在于每秒写回是一秒写入一次磁盘,操作系统写回磁盘的时间无法控制,如果发生宕机每秒写回可能只是丢失一秒的数据,而操作系统控制写回丢失的数据会更多。

综上所述:always策略属于同步写回,可靠性高,数据基本不丢失。但是每个写命令都要入磁盘,性能影响较大。

eversec策略属于每秒写回,性能适中。但是宕机会丢失1秒内未写入磁盘的数据。

no策略属于操作系统控制写回,性能最好。但是宕机时数据丢失最多。

所以想要高性能可以选择No策略,如果需要数据高可靠性就选择always策略。如果允许数据有一点丢失,又不希望性能有太大影响就选择eversec策略。

三、AOF重写机制

AOF主要是以文件的形式来接收所有的写命令,因此随着写命令的增多,aof文件会越来越大。文件越来越大也是我们需要考虑的因素,因为他会对性能造成以下的问题:

(1)aof文件本身就是有大小限制的,所以不可以无限制的存储所有的命令。

(2)文件过大,继续增加写命令的话,效率也会变低。

(3)宕机后想要根据这个aof来恢复数据的话,由于aof文件很大会导致恢复的过程变慢,影响redis的恢复。

所以当aof文件过大的情况下,引入了aof重写机制。

aof重写机制就是在重写时,redis会创建一个新的aof文件。即原始的aof文件是追加的命令存储在aof中,比如对同一key,做了三次修改命令,原始的aof就会有三条记录,而重写机制只会保留三条合并后的一条命令。

举例:

1、String的修改操作

set testKey "1";

set testKey "2";

set testKey "100";

重写前的aof会有三条记录,而重写的aof只有合并后的一条记录即set testKey "100"。

2、List的修改操作

LPUSH testList "A","B";

LPUSH testList "C","D";

RPOP testList;

RPOP testList;

LPUSH testList "E","F";

重写前的aof会有5条命令,而重写后的aof只有合并后的一条记录即LPUSH testList "F","E","D","C";

这样一来在对redis宕机后恢复数据时,根据重写后的aof来进行恢复时,执行的命令就少了很多,提高了恢复数据的效率。

四、AOF重写工作原理

为了保证AOF重写时,不阻塞redis的主线程,他采用了“一个拷贝,两处日志”的方式。首先aof重写的工作是由后台子进程bgrewriteof来完成的。

“一个拷贝”指,每次执行重写工作,主线程会fork出bgrewriteof子进程,并且在这一步也会把主线程中的最新数据拷贝给bgrewriteof。

“两处日志”:第一处指原始的aof日志,因为此时的redis还是在运行的,如果有新的写请求被执行,那么还是会追加的形式放到原始的aof日志或aof日志缓冲区中。第二处指新的aof重写日志,等到所有拷贝数据的所有更新记录重写完成后,重写日志记录的日志也会变为新的aof日志,取代原始重写前的aof日志。

工作原理图:

综上所述,每次aof重写时,都会使用一个数据拷贝来用于重写,然后使用两个日志来保证在重写过程中新写入的数据不会丢失。并且整体操作采用子进程的方式来操作,并不会阻塞主线程。

五、总结

redis避免数据丢失的其中一种持久化方式是AOF日志,通过逐一记录更新命令的方式写入到aof日志中进行持久化。如果出现宕机需要恢复数据时就根据aof日志逐一执行命令来恢复数据。

aof日志的写入方式redis提供了三种方式,总结如下:

想要高性能可以选择No策略,如果需要数据高可靠性就选择always策略。如果允许数据有一点丢失,又不希望性能有太大影响就选择eversec策略。

另外为了防止出现日志文件过大造成的性能问题,redis也提供了aof重写机制,根据redis中的最新数据将多个命令合并成一条命令。重写机制也采用了从主线程中fork出一个bgrewriteaof子进程的方式来进行,防止阻塞redis主线程。

问题一:AOF日志重写的时候,是由bgrewriteaof子进程来完成的,不用主线程参与。但是这个过程有没有潜在的阻塞风险呢?

(1)主线程将数据拷贝给子进程时会阻塞主线程。redis实例内存越大,fork执行时间就会越长。

(2)bgrewriteaof子进程和主线程是共享内存的。当主线程收到新的更新操作时,主线程会申请新的内存空间用来保存更新的数据,如果操作的是bigkey,那么主线程会因为申请大空间而面临阻塞风险,因为操作系统在分配内存空间时,有查找和锁的开销,会导致阻塞。

问题二:AOF重写为什么不在原始aof日志中。

如果都用aof重写前日志的话,由于主线程要写入日志,bgrewriteof子线程也要写入日志,这两者就会竞争文件系统的锁,会影响主线程的性能。

问题三:主线程、子进程和后台线程的联系与区别?

从操作系统的角度来看,进程一般是指资源分配单元,例如一个进程拥有自己的堆、栈、虚拟空间(页表)、文件描述符等。

线程一般是指CPU进行调度和执行的实体。

主进程/主线程指,如果一个进程启动后,没有再创建额外的线程,那么这样的进程就被称为主进程或主线程。

综上所述,以redis为例,redis本身就是一个进程,但是redis也没有再依赖其他的线程,主要的工作比如接收客户端请求、处理写操作请求都在redis中。所以redis本身这个进程是完成主要的工作,所以可以叫做主进程或主线程。

但是在主线程中,我们可以通过fork操作来创建子进程,比如上文提到的bgrewriteaof子进程就是通过fork创建出来的。

而后台线程,是指相对于完成主要工作的主线程来说的,例如异步删除任务等线程操作,可以称为后台线程。redis可通过pthread_create来创建线程。

问题四:对于重写过程中,新的写入数据如果更新到重写后的aof日志中?

aof的工作原理如下:

(1)redis主线程执行fork操作来创建bgrewriteaof子进程,并拷贝了主线程中最新的内存数据。

(2)子进程根据拷贝后的内存数据写入到临时文件中。

(3)对于工作中所有新的写入命令,主线程会将他们累积到一个内存缓存中,一边将新的命令写入到原始的aof日志中,保证原始的aof日志和新的写入请求数据不丢失。

(4)重写工作完成,会给主线程发送一个信号,主线程收到信号后将内存缓存中的所有数据追加到新的aof日志中。

(5)之后所有的命令都会追加到新的aof日志中。

最后,redis持久化的方式并不能完全只靠aof一种持久化方式,下一篇我们来学习下另一种持久化方式,rdb快照日志。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-12-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码云大作战 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档