这篇文章已经收录到Github,推荐阅读,我在工作中也经常打交道。说到持久化,就会想到RDB和AOF,不过还有一些细节可以讨论。例如:为什么fork这么快?AOF是如何提高写性能的?等问题。这些问题将在本文中得到解答。摘要Redis是一种高性能的非关系型数据库,被许多公司使用。最重要的特性之一是它支持持久性。本文将深入介绍Redis持久化的原理,包括RDB和AOF的实现。Redis持久化介绍Redis持久化分为两种:RDB(RedisDataBase)和AOF(AppendOnlyFile)。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个键被改变”的条件时自动保存一次数据集。save10100Fork函数和写时复制在Redis中,fork函数用于创建子进程。Redis的使用场景通常读操作多,写操作少,fork功能可以利用Linux操作系统的COW机制,让父子进程共享内存,从而减少内存占用,并避免不必要的数据重复。我们可以使用manfork查看文档。翻译:在Linux下,fork()是使用写时复制页实现的,因此它的唯一成本是复制父页表并为子页创建独特的任务结构所需的时间和内存。简单的说,fork()函数只复制指针,不复制数据,所以速度非常快。需要注意的是fork进程的主进程是阻塞的,fork完成后不会阻塞。在Redis中,当进行RDB持久化操作时,Redis会调用fork函数创建一个子进程,然后该子进程负责将数据写入磁盘。为了避免父子进程同时修改内存中的数据导致数据不一致。Redis启用了写时复制机制。这样,当父进程修改内存中的数据时,Linux内核会将这部分内存复制给子进程,从而保证父子进程之间的数据相互独立。画了一个简单的示意图:当没有发生写操作时,子进程和父进程的地址相同,当发生写操作时,会复制一块新的内存区域,实现父子进程的隔离。通过使用fork功能和写时复制机制,Redis可以高效的进行RDB持久化操作,同时不会对Redis在运行过程中的性能造成太大的影响。同时,该方法也提供了一种简单有效的机制来保护Redis数据的一致性和可靠性。缺点:RDB需要频繁fork子进程将数据集保存到硬盘。当数据集比较大时,fork过程非常耗时,可能导致Redis无法在毫秒级内响应客户端请求。当它很大时,fork过程可能会持续几秒钟。考虑copy-on-write上面的过程好像有问题。例如,有一个键值对k1a。此时bgsave正在进行,客户端发送请求,主进程生成写操作集合k1b。由于copy-on-write,子进程中k1的值仍然是a值。最后的坚持也是一个。为什么不直接持久化新值而是持久化旧值呢?写时复制有什么意义?主要有两个原因:其实Redis出于性能的考虑,将数据持久化到内存中是一个顺序写操作,而RDB备份有一个过程,由子过程完成。子进程备份RDB是一个顺序写入的进程。如果任何时候主进程的所有写请求都记录在RDB文件中,那么理论上的updatekey可能在任何位置。这是一个低性能的随机写入过程。其次,如果主进程一直在写入,那么这个RDB备份就一直在写入主进程写入的新值,永远不会停止。RDB相关配置保存:指定RDB持久化操作的条件。当Redis的数据发生变化时,经过指定的时间(秒)和变化次数(changes)后,Redis会自动进行一次RDB操作。例如save360010000表示如果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:这是Redis4.0新增的配置项,用于指定replica节点在与primary节点断开连接后是否继续向客户端提供旧数据。当设置为yes时,复制节点与主节点断开连接后,该节点将继续向客户端提供旧数据,直到重新连接到主节点并同步全新的数据;当设置为no时,复制节点会立即停止向客户端提供数据,等待重新连接主节点并同步数据。需要注意的是,当replica-serve-stale-data设置为yes时,可能会出现一定的数据不一致问题,所以建议只在特定场景下使用。repl-diskless-sync:这是Redis2.8引入的配置项,用于指定复制节点在进行初始全量同步(即从主节点获取所有数据)时是否采用无盘同步方式。设置为yes时,复制节点将直接通过网络获取master节点的数据,不会将数据存储到本地磁盘;当设置为no时,copy节点会先将master节点的数据保存到本地磁盘,然后再进行sync操作。使用无盘同步可以避免磁盘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文件例子。*号:表示参数个数,后面是参数的长度和值。$number:表示参数的长度,后面是参数的值。事实上,AOF文件中保存的所有命令都遵循相同的格式,即以*开头表示参数个数,以$开头表示参数长度,后面是参数的值。AOF文件修复服务器可能会在程序写入AOF文件时停止,导致AOF文件出错。可以使用Redis自带的redis-check-aof程序修复原来的AOF文件:AOF文件的大小和提高读写性能。Redis的AOF重写机制采用了类似复制的方式。首先将内存中的数据快照保存到临时文件中,然后遍历临时文件,只保留最终状态命令,生成新的AOF文件。具体来说,Redis进行AOF重写可以分为以下几个步骤:启动AOF重写进程,并向客户端返回提示信息。创建临时文件,将当前数据库中的键值对写入临时文件。在创建的临时文件中,所有的写入命令都转换成Redis内部的表示格式,即用一系列的Redis命令来表示一个操作,比如用SET命令来表示对某个对象的赋值操作钥匙。压缩临时文件,去除多余的空格和换行符等,减少文件大小。将压缩后的内容写入一个新的AOF文件。停止向旧AOF文件写入命令,并将新AOF文件的文件名替换为旧AOF文件的文件名。结束AOF改写过程,并向客户端发送完成提示信息。通过AOF重写机制,Redis可以在不停止服务的情况下减小AOF文件的大小,提高读写性能,同时也保证了数据的一致性。Redis提供了命令BGREWRITEAOF来手动触发AOF重写。可以在Redis客户端执行该命令,启动AOF重写过程。Redis2.2需要手动执行BGREWRITEAOF命令;Redis2.4可以自动触发AOF重写。具体操作步骤如下:打开redis-cli命令行工具,连接Redis服务。执行BGREWRITEAOF命令启动AOF重写过程。$redis-cli127.0.0.1:6379>BGREWRITEAOFRedis会返回一个后台任务ID,表示AOF重写任务已经启动。127.0.0.1:6379>BGREWRITEAOFBackgroundappendonlyfilerewritingstartedbypid1234可以使用INFOPERSISTENCE命令查看当前AOF文件的大小和重写进程的状态,等待重写完成即可。127.0.0.1:6379>INFOPERSISTENCE#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缓冲区。AOFbuffer中的内容按照配置的规则持久化到磁盘。可以通过配置项appendfsync调整持久化规则。AOF重写缓冲区是用于对AOF文件进行重写操作的缓冲区。AOF改写操作是将已有的AOF文件改写为最小化的新AOF文件的操作。AOF重写操作的目的是在加快恢复速度的同时减小AOF文件的大小。AOF重写缓冲区在AOF重写时开始启用,Redis服务器主进程执行完写命令后会同时追加到AOF缓冲区和AOF重写缓冲区。需要注意的是,AOFbuffer和AOFrewritebuffer是不同的概念,但是都使用了Redis的内存缓冲机制,都会影响Redisserver的性能。因此,在实际使用中,应合理配置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的同步机制。可选值有3个: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默认会记录上次rewrite的AOF大小,默认配置是当AOFfilesize上次重写后文件大小翻倍且文件大于64M时触发。aof-use-rdb-preamble:Redis4版本的新特性,混合持久化。AOF改写时是否启用增量同步,AOF改写时该配置项是否使用RDB文件内容。默认为否。如果设置为yes,则将一个RDB文件的内容添加到AOF文件头中,这样可以尽可能的减小AOF文件的体积,也便于数据恢复。我们比较熟悉的是数据库的预写日志(WAL)。也就是说,在真正写入数据之前,先将修改后的数据记录到日志文件中,以便在发生故障时可以恢复。但是,AOF日志恰恰相反。这是写完后的日志。“写入后”是指Redis先执行命令,将数据写入内存,然后记录日志。为什么?为了避免额外的检查开销,Redis在记录AOF中的日志时,不会先检查这些命令的语法。因此,如果先记录日志再执行命令,则可能会在日志中记录错误的命令,Redis在使用日志恢复数据时可能会出错。post-writelog的方法是让系统先执行命令,只有命令执行成功,才会记录在日志中,否则系统直接报错给客户端。所以Redis使用post-writelogging的一大好处就是可以避免记录错误的命令。另外,AOF还有一个好处:在命令执行后记录日志,因此不会阻塞当前的写操作。但是,AOF也有两个潜在的风险。首先,如果一个命令刚刚执行完,还没登录就系统崩溃了,有丢失命令和对应数据的风险。如果此时使用Redis作为缓存,也可以重新从后端数据库中读取数据进行恢复。但是,如果直接使用Redis作为数据库,此时由于日志中没有记录命令,所以无法使用日志进行恢复。向上。其次,AOF虽然避免了对当前命令的阻塞,但是可能会给下一次操作带来阻塞风险。这是因为AOF日志也是在主线程中执行的。如果日志文件写入磁盘时磁盘写压力大,会导致写入磁盘很慢,无法进行后续操作。混合持久化过去,Redis用户经常因为RDB持久化和AOF持久化的优缺点不同而陷入两难境地:RDB持久化可以快速存储和恢复数据,但当服务器宕机时可能会丢失大量数据数据。AOF持久化可以有效提高数据的安全性,但是需要大量的时间来存储和恢复数据。为了让用户同时拥有以上两种持久化的优势,Redis4.0引入了一种“鱼和熊掌兼得”的持久化方案——RDB-AOF混合持久化:这种持久化可以重写通过AOF创建一个同时包含RDB数据和AOF数据的AOF文件,其中RDB数据位于AOF文件的开头,它们存储着服务器开始执行重写操作时的数据库状态。至于那些在rewrite操作执行后执行的Redis命令,会继续以AOF格式追加到AOF文件的末尾,即RDB数据之后。也就是说,启用混合持久化后,AOF文件中的内容:前半部分是二进制RDB内容,后面是AOF添加的数据,AOF位于两个RDB之间。格式类似如下:(binary)RDBAOF(binary)在当前版本的RDB中,RDB-AOF混合持久化功能默认是关闭的。为了启用该功能,用户不仅需要启用AOF持久化功能,还需要将aof-use-rdb-preamble选项的值设置为true。appendonlyyesaof-use-rdb-preambleyes如何选择合适的持久化方式当你想选择适合自己应用的持久化方式时,需要考虑以下因素:数据的实时性和一致性:如果你是真正的-对时间和数据的一致性有很高的要求,所以AOF可能是更好的选择。如果你对数据的实时性和一致性要求不高,希望快速加载数据,减少磁盘空间占用,那么RDB可能更适合你的应用。由于RDB文件是二进制格式,所以文件紧凑,Redis重启时加载数据速度快。Redis性能要求:如果你对Redis的性能要求很高,关闭持久化功能也是一种选择。因为持久化功能可能会影响Redis的性能,但一般不建议这样做。小结通过本文的介绍,我们了解了Redis的两种持久化方式,RDB和AOF,它们都有着自己独特的优势。我们应该根据项目规模、数据量和业务需求制定不同的持久化策略。本文就到此为止,感谢大家的阅读,如果本博客有什么错误或者建议,欢迎大家留言指正。文章持续更新中,大家可以关注公众号第一时间阅读。
