本文已收录至Github,举荐浏览 Java随想录
微信公众号:Java随想录
写在后面
Redis的长久化,这部分的知识点不仅求职面试的时候是重点,工作中也是常常打交道。说起长久化都会想到RDB和AOF,然而外面有些细节是能够开展去聊的。比方:为什么 fork速度这么快?AOF是如何进步写入性能的?等问题。对这些疑难本文都会有所解答。
摘要
Redis是许多公司都在应用的一款高性能、非关系型数据库。其中最为重要的一个个性就是它反对长久化。本文将深刻介绍Redis长久化原理,包含RDB和AOF两种形式的实现。
Redis长久化介绍
Redis长久化分为两种:RDB(Redis DataBase)和AOF(Append Only File)。RDB是指将Redis内存中的数据定期写入磁盘上的一个快照文件中;而AOF则是以追加的形式记录Redis执行的每一条写命令。你也能够同时开启两种长久化形式,在这种状况下,当redis重启的时候会优先载入AOF文件来复原原始的数据。
你兴许会问,为什么须要长久化呢?因为Redis作为一款内存数据库,在过程异样退出或服务器断电之后,所有的数据都将隐没。如果没有长久化性能,无奈保证数据的持久性,那么这样的数据库还有什么用呢?
因而,Redis提供了长久化性能,以确保数据的可靠性和持久性。接下来,咱们将别离介绍RDB和AOF的实现原理。
RDB原理
RDB是Redis默认的长久化形式,它将Redis在内存中的数据定期写入到硬盘中,生成一个快照文件。快照文件是一个二进制文件,蕴含了Redis在某个工夫点的所有数据。
RDB的长处是疾速、简略,实用于大规模数据备份和复原。然而,RDB也有毛病,例如数据可能会失落,因为Redis只会在指定的工夫点生成快照文件。如果在快照文件生成之后,但在下一次快照文件生成之前服务器宕机,那么这期间的数据就会失落。
因为RDB文件是以二进制格局保留的,因而它十分紧凑,并且在Redis重启时能够迅速地加载数据。相比于AOF,RDB文件个别会更小。
RDB长久化有两种形式:
- 手动
- 主动
手动形式通过SAVE命令或BGSAVE命令进行。SAVE命令会阻塞Redis服务器,直到快照文件生成实现。BGSAVE命令会fork一个子过程(留神是子过程,不是子线程)在后盾生成快照文件,不会阻塞Redis服务器。
主动形式则是在配置文件中设置, 让它在“ N 秒内数据集至多有 M 个改变”这一条件被满足时, 主动保留一次数据集。
比如说,以下设置会让 Redis 在满足 “10秒内有至多100 个键被改变” 这一条件时, 主动保留一次数据集。
save 10 100
Fork函数与写时复制
在 Redis 中,fork 函数被用于创立子过程。Redis 的应用场景中通常有大量的读操作和较少的写操作,而 fork 函数能够利用 Linux 操作系统的写时复制(Copy On Write,即 COW)机制,让父子过程共享内存,从而缩小内存占用,并且防止了没有必要的数据复制。
咱们能够应用 man fork 来查看下阐明文档。
翻译:在Linux下,fork()是应用写时复制的页面实现的,所以它惟一的代价是复制父过程的页表以及为子过程创立独特的工作构造所需的工夫和内存。
简略来说就是 fork()函数复制的是指针,不会复制数据,所以速度很快。
须要留神的是,fork的这个过程主过程是阻塞的,fork完之后不阻塞。
在 Redis 中,当执行 RDB 长久化操作时,Redis 会调用 fork 函数创立子过程,而后由子过程负责将数据写入到磁盘中。为了防止父子过程同时对内存中的数据进行批改导致数据不统一。Redis 会启用写时复制机制。这样,当父过程批改内存中的数据时, Linux 内核会将该局部内存复制一份给子过程应用,从而保障父子过程间的数据相互独立。
简略画了个示意图:
当没有产生写的时候,子过程和父过程指向地址是一样的,产生写的时候,就会拷贝出一块新的内存区域,实现父子过程隔离。
通过应用 fork 函数和写时复制机制,Redis 能够高效地执行 RDB 长久化操作,并且不会对 Redis 运行过程中的性能造成太大的影响。同时,这种形式也提供了一种简略无效的机制来爱护 Redis 数据的一致性和可靠性。
毛病:RDB 须要常常fork子过程来保留数据集到硬盘上,当数据集比拟大的时候,fork的过程是十分耗时的,可能会导致Redis在一些毫秒级内不能响应客户端的申请,数据集很大的时候,fork过程可能会继续数秒。
对于写时复制的思考
上述流程貌似有个问题。比方,有个键值对 k1 a 。此时正在bgsave,客户端发来一个申请,主过程产生写操作set k1 b,因为写时复制,此时子过程里k1值还是值a。最终长久化的也是a。
为什么不间接长久化新值而长久化旧值?写时复制的意义是什么?
次要有2点起因:
- 其实Redis为了性能思考,将内存的数据长久化是一个程序写的操作,RDB备份是有一个过程的,这个过程是由子过程实现。子过程备份RDB是一个程序写的过程,如果主过程的所有写入申请都随时记录到RDB文件中,那么实践更新key可能在任何地位,这是个随机写的过程,性能低。
- 其次如果主过程始终在写入的话,那么这次RDB备份始终都在写主过程写入的新值,永远不会进行。
RDB相干配置
- save:指定 RDB 长久化操作的条件。当 Redis 的数据发生变化,并且通过指定的工夫(seconds)和变动次数(changes)后,Redis 会主动执行一次 RDB 操作。例如,save 3600 10000 示意如果 Redis 的数据在一个小时内产生了至多 10000 次批改,那么 Redis 将执行一次 RDB 操作。
- stop-writes-on-bgsave-error:指定在 RDB 长久化过程中如果呈现谬误是否进行写入操作。如果设置为 yes,当 Redis 在执行 RDB 操作时遇到谬误时,Redis 将进行承受写入操作;如果设置为 no,Redis 将持续承受写入操作。
- rdbcompression:指定是否对 RDB 文件进行压缩。如果设置为 yes,Redis 会在生成 RDB 文件时对其进行压缩,从而缩小磁盘占用空间;如果设置为 no,Redis 不会对生成的 RDB 文件进行压缩。
- rdbchecksum:指定是否对 RDB 文件进行校验和计算。如果设置为 yes,在保留 RDB 文件时,Redis 会计算一个 CRC64 校验和并将其追加到 RDB 文件的开端;在加载 RDB 文件时,Redis 会对文件进行校验和验证,以确保文件没有受到损坏或篡改。
- replica-serve-stale-data:这是 Redis 4.0 中新增的一个配置项,用于指定复制节点在与主节点断开连接后是否持续向客户端提供旧数据。当设置为 yes 时,在复制节点与主节点断开连接后,该节点将持续向客户端提供旧数据,直到从新连贯上主节点并且同步齐全新的数据为止;当设置为 no 时,复制节点会立刻进行向客户端提供数据,并且期待从新连贯上主节点并同步数据。须要留神的是,当 replica-serve-stale-data 设置为 yes 时,可能会存在肯定的数据不一致性问题,因而倡议仅在特定场景下应用。
- repl-diskless-sync:这是 Redis 2.8 中引入的一个配置项,用于指定复制节点在进行首次全量同步(即从主节点获取全副数据)时是否采纳无盘同步形式。当设置为 yes 时,复制节点将通过网络间接获取主节点的数据,并且不会将数据存储到本地磁盘中;当设置为 no 时,复制节点将先将主节点的数据保留到本地磁盘中,而后再进行同步操作。采纳无盘同步形式能够防止磁盘 IO 操作对系统性能的影响,但同时也会减少网络负载和内存占用。因而,应该依据具体的场景和需要抉择适合的同步形式。
AOF原理
AOF长久化是依照Redis的写命令程序将写命令追加到磁盘文件的开端。AOF是一种基于日志的长久化形式,它保留了Redis服务器所有写入操作的日志记录,以保证数据的持久性、可靠性和完整性。AOF长久化技术的核心思想是将Redis服务器执行的所有写命令追加到一个文件中。当Redis服务器重新启动时,能够通过从新执行AOF文件来复原服务器的状态。
AOF有个比拟好的劣势是能够复原误操作。举个例子,如果你不小心执行了 FLUSHALL 命令,但只有 AOF 文件未被重写,那么只有进行服务器,移除 AOF 文件开端的 FLUSHALL 命令,并重启 Redis ,就能够将数据集复原到 FLUSHALL 执行之前的状态,重写了就没方法了。
AOF长久化配置
Redis的AOF长久化配置频率可通过appendfsync
参数进行管制。该参数有以下三个选项:
always
: 每次有数据批改都立刻写入磁盘,是最平安的选项。everysec
: 每秒钟写入一次,性能和平安之间做了一个均衡。no
: 从不被动写入,齐全依附操作系统本身的缓存机制来决定何时将数据写入磁盘。
默认状况下,Redis的appendfsync
参数设置为everysec
。如果须要进步长久化安全性,能够将其改为always
;如果更关注性能,则能够将其改为no
。然而须要留神的是,应用no
可能会导致数据失落的危险,倡议在利用场景容许的状况下审慎应用。
AOF文件解读
这是一个简略的AOF文件示例。
- *号:示意参数个数,前面紧跟着参数的长度和值。
- $号:示意参数长度,前面紧跟着参数的值。
实际上AOF文件中保留的所有命令都遵循雷同的格局,即以*结尾示意参数个数,$结尾示意参数长度,其后紧跟着参数的值。
AOF文件修复
服务器可能在程序正在对 AOF 文件进行写入时停机,造成AOF 文件出错。
能够应用 Redis 自带的 redis-check-aof 程序,对原来的 AOF 文件进行修复:
$ redis-check-aof –fix
AOF重写
Redis的AOF重写机制指的是将AOF文件中的冗余命令删除,以减小AOF文件的大小并进步读写性能的过程。
Redis的AOF重写机制采纳了相似于复制的形式,首先将内存中的数据快照保留到一个临时文件中,而后遍历这个临时文件,只保留最终状态的命令,生成新的AOF文件。
具体来说,Redis执行AOF重写能够分为以下几个步骤:
- 开始AOF重写过程,向客户端返回一个提示信息。
- 创立一个临时文件,并将以后数据库中的键值对写入到临时文件中。
- 在创立的临时文件中将所有的写命令都转换成Redis外部的示意格局,即应用一系列的Redis命令来示意一个操作,例如应用SET命令来示意对某个键进行赋值操作。
- 对临时文件进行压缩,去掉多余的空格和换行符等,减小文件体积。
- 将压缩后的内容写入到新的AOF文件中。
- 进行写入命令到旧的AOF文件,并将新的AOF文件的文件名替换为旧的AOF文件的文件名。
- 完结AOF重写过程,并向客户端发送实现提示信息。
通过AOF重写机制,Redis能够在不进行服务的状况下减小AOF文件的大小,进步读写性能,同时也能够保证数据的一致性。
Redis提供了手动触发AOF重写的命令BGREWRITEAOF。能够在Redis的客户端中执行该命令来启动AOF重写过程。Redis 2.2 须要本人手动执行 BGREWRITEAOF 命令; Redis 2.4 则能够主动触发 AOF 重写。
具体操作步骤如下:
- 关上redis-cli命令行工具,连贯到Redis服务。
执行BGREWRITEAOF命令,启动AOF重写过程。
$ redis-cli127.0.0.1:6379> BGREWRITEAOF
Redis会返回一个后台任务的ID,示意AOF重写工作曾经开始。
127.0.0.1:6379> BGREWRITEAOFBackground append only file rewriting started by pid 1234
能够应用INFO PERSISTENCE命令查看以后AOF文件的大小和重写过程的状态,期待重写实现即可。
127.0.0.1:6379> INFO PERSISTENCE# Persistenceaof_enabled:1aof_rewrite_in_progress:1aof_rewrite_scheduled:0aof_last_rewrite_time_sec:0aof_current_rewrite_time_sec:14aof_last_bgrewrite_status:okaof_last_write_status:ok
须要留神的是,执行BGREWRITEAOF命令可能会占用较多的CPU和内存资源,因而在生产环境中须要审慎应用,并确保有足够的系统资源反对。同时,即便手动触发AOF重写,Redis也会在满足肯定条件时主动触发AOF重写,以保障AOF文件的大小和性能。
在版本号大于等于 2.4 的 Redis 中,BGSAVE 执行的过程中,不能够执行 BGREWRITEAOF 。反过来说,在 BGREWRITEAOF 执行的过程中,也不能够执行 BGSAVE。这能够避免两个 Redis 后盾过程同时对磁盘进行大量的 I/O 操作。
AOF缓冲区与AOF重写缓存区
在Redis中,AOF缓冲区和AOF重写缓冲区是两个不同的概念。
AOF缓冲区是一个用于暂存须要写入AOF文件的命令的缓冲区。在Redis解决客户端发来的写命令时,如果开启了AOF长久化性能,则该命令将被先写入到AOF缓冲区。AOF缓冲区中的内容通过配置的规定长久化到磁盘上。长久化规定能够通过配置项appendfsync
来调整。
AOF重写缓冲区是一个用于执行AOF文件的重写操作的缓冲区。AOF重写操作是一种将现有AOF文件重写成最小化的新AOF文件的操作。AOF重写操作的目标是缩小AOF文件的大小,同时放慢复原速度。AOF重写缓存区在AOF重写时开始启用,Redis服务器主过程在执行完写命令之后,会同时将这个写命令追加到AOF缓冲区和AOF重写缓冲区。
须要留神的是,AOF缓冲区和AOF重写缓冲区是不同的概念,但它们都应用了Redis的内存缓冲机制,并且都会影响Redis服务器的性能。因而,在理论应用中,应该合理配置AOF缓冲区大小和AOF重写规定,以确保Redis服务器的失常运行和高性能。
AOF缓冲区能够代替AOF重写缓冲区吗?
AOF缓冲区不能够代替AOF重写缓冲区的起因是AOF重写缓冲区记录的是从重写开始后的所有须要重写的命令,而AOF缓冲区可能只记录了局部的命令(如果写回的话,AOF缓存区的数据就会生效被失落,因此只会保留一部分的命令,而AOF重写缓存区不会)。
AOF缓冲区就次要是Redis用来解决主过程执行命令速度与磁盘写入速度不同步所设置的,通过AOF缓冲区能够无效地防止频繁对硬盘进行读写,进而晋升性能。Redis在AOF长久化的时候,会先把命令写入到AOF缓冲区,而后通过回写策略来写入硬盘AOF文件。
AOF相干配置
在 Redis 的配置文件 redis.conf 中,能够通过以下配置项来设置 AOF 相干参数:
- appendonly:该配置项用于开启或敞开 AOF,默认为敞开。若开启了 AOF,Redis 会在每次执行写命令时,将命令追加到 AOF 文件开端。
- appendfilename:用于设置 AOF 文件名,默认为 appendonly.aof。
appendfsync:该配置项用于设置 AOF 的同步机制。有三种可选值:
- always:示意每个写命令都要同步到磁盘,安全性最高,然而性能较差。
- everysec:示意每秒同步一次,是默认选项,既能保障数据安全,又具备较好的性能。
- no:示意不进行同步,而是由操作系统决定何时将缓冲区中的数据同步到磁盘上,性能最好,然而安全性较低。
- auto-aof-rewrite-percentage和auto-aof-rewrite-min-size:这两个配置项用于设置 AOF 重写规定。当 AOF 文件大小超过 auto-aof-rewrite-min-size 设置的值,并且 AOF 文件增长率达到 auto-aof-rewrite-percentage 所定义的百分比时,Redis 会启动 AOF 重写操作。auto-aof-rewrite-percentage:默认值为100,以及auto-aof-rewrite-min-size:64mb 配置,也就是说默认Redis会记录上次重写时的AOF大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发。
- aof-use-rdb-preamble:Redis 4版本新个性,混合长久化。AOF重写期间是否开启增量式同步,该配置项在AOF重写期间是否应用RDB文件内容。默认是no,如果设置为yes,在AOF文件头退出一个RDB文件的内容,能够尽可能的减小AOF文件大小,同时也不便复原数据。
写后日志
咱们比拟相熟的是数据库的写前日志(Write Ahead Log,WAL),也就是说,在理论写数据前,先把批改的数据记到日志文件中,以便故障时进行复原。不过,AOF 日志正好相同,它是写后日志,“写后”的意思是 Redis 是先执行命令,把数据写入内存,而后才记录日志。
why?
为了防止额定的查看开销,Redis 在向 AOF 外面记录日志的时候,并不会先去对这些命令进行语法查看。所以,如果先记日志再执行命令的话,日志中就有可能记录了谬误的命令,Redis 在应用日志复原数据时,就可能会出错。
而写后日志这种形式,就是先让零碎执行命令,只有命令能执行胜利,才会被记录到日志中,否则,零碎就会间接向客户端报错。所以,Redis 应用写后日志这一形式的一大益处是,能够避免出现记录谬误命令的状况。
除此之外,AOF 还有一个益处:它是在命令执行后才记录日志,所以不会阻塞以后的写操作。
不过,AOF 也有两个潜在的危险。
首先,如果刚执行完一个命令,还没有来得及记日志就宕机了,那么这个命令和相应的数据就有失落的危险。如果此时 Redis 是用作缓存,还能够从后端数据库从新读入数据进行复原,然而,如果 Redis 是间接用作数据库的话,此时,因为命令没有记入日志,所以就无奈用日志进行复原了。
其次,AOF 尽管防止了对以后命令的阻塞,但可能会给下一个操作带来阻塞危险。这是因为,AOF 日志也是在主线程中执行的,如果在把日志文件写入磁盘时,磁盘写压力大,就会导致写盘很慢,进而导致后续的操作也无奈执行了。
混合长久化
在过来, Redis 用户通常会因为 RDB 长久化和 AOF 长久化之间不同的优缺点而陷入两难的抉择当中:
- RDB 长久化可能疾速地贮存和复原数据,然而在服务器停机时可能会失落大量数据。
- AOF 长久化可能无效地进步数据的安全性,然而在贮存和复原数据方面却要消耗大量的工夫。
为了让用户可能同时领有上述两种长久化的长处, Redis 4.0 推出了一个“鱼和熊掌兼得”的长久化计划 —— RDB-AOF 混合长久化: 这种长久化可能通过 AOF 重写操作创立出一个同时蕴含 RDB 数据和 AOF 数据的 AOF 文件, 其中 RDB 数据位于 AOF 文件的结尾, 它们贮存了服务器开始执行重写操作时的数据库状态。至于那些在重写操作执行之后执行的 Redis 命令, 则会持续以 AOF 格局追加到 AOF 文件的开端, 也即是 RDB 数据之后。
说就是说开启混合长久化之后,AOF文件中的内容:前半部分是二进制的RDB内容,前面跟着AOF减少的数据,AOF位于2次RDB之间。格局相似上面这样:
(二进制)RDB AOF(二进制)RDB
在目前版本中, RDB-AOF 混合长久化性能默认是处于敞开状态的, 为了启用该性能, 用户不仅须要开启 AOF 长久化性能, 还须要将 aof-use-rdb-preamble
选项的值设置为 true。
appendonly yesaof-use-rdb-preamble yes
如何抉择适合的长久化形式
当你想抉择适宜你的应用程序的长久化形式时,你须要思考以下几个因素:
- 数据的实时性和一致性:如果您对数据的实时性和一致性有很高的要求,则AOF可能是更好的抉择。
- 如果您对数据的实时性和一致性要求不太高,并且心愿能疾速地加载数据并缩小磁盘空间的应用,那么RDB就可能更适宜您的应用程序。因为RDB文件是二进制格局的,所以它很紧凑,并且在Redis重启时能够迅速地加载数据。
- Redis的性能需求:如果您对Redis的性能有很高的要求,那么敞开长久化性能也是一个抉择。因为长久化性能可能会影响Redis的性能,然而个别不倡议这么做。
总结
通过本文的介绍,咱们理解了Redis长久化的两种形式,RDB和AOF,它们都有本人独特的劣势,咱们应该依据我的项目大小、数据量和业务需要制订不同的长久化策略。
本篇文章就到这里,感激浏览,如果本篇博客有任何谬误和倡议,欢送给我留言斧正。文章继续更新,能够关注公众号第一工夫浏览。