您的位置:首页 > 文旅 > 美景 > 免费b站动漫推广网站2023_专业定制网站_引流最好的推广方法_seo包年优化平台

免费b站动漫推广网站2023_专业定制网站_引流最好的推广方法_seo包年优化平台

2025/1/7 11:17:49 来源:https://blog.csdn.net/tus00000/article/details/143195804  浏览:    关键词:免费b站动漫推广网站2023_专业定制网站_引流最好的推广方法_seo包年优化平台
免费b站动漫推广网站2023_专业定制网站_引流最好的推广方法_seo包年优化平台

除了RDB持久化外,Redis还提供了AOF(Append Only File)持久化功能。AOF持久化是通过保存Redis服务器执行的写命令来记录数据库状态的:
在这里插入图片描述
例如,我们对空白数据库执行以下写命令,数据库中将包换三个键值对:
在这里插入图片描述
AOF持久化的方法是将服务器执行的SET、SADD、RPUSH三个命令保存到AOF文件中。

被写入AOF文件的所有命令都是以Redis的命令请求协议格式保存的,因为Redis的命令请求协议是纯文本格式,所以我们可以直接打开一个AOF文件,观察里面的内容。

例如,对于上面执行的三个写命令来说,服务器将产生包含以下内容的AOF文件:
在这里插入图片描述
在这个AOF文件里,除了用于指定数据库的SELECT命令是服务器自动添加的之外,其他都是我们通过客户端发送的命令。

服务器启动时,可通过载入和执行AOF文件中保存的命令来还原服务器关闭前的数据库状态,以下是服务器载入AOF文件并还原数据库状态时打印的日志:
在这里插入图片描述
11.1 AOF持久化的实现

AOF持久化的实现可分为命令追加(append)、文件写入、文件同步(sync)三个步骤。

11.1.1 命令追加

当AOF持久化功能打开时,服务器在执行完一个命令后,会以协议格式将命令追加到服务器状态的aof_buf缓冲区末尾:

struct redisServer {// ...// AOF缓冲区sds aof_buf;// ...
};

例如,客户端向服务器发送以下命令:
在这里插入图片描述
那么服务器在执行完SET命令后,会将以下协议内容追加到aof_buf缓冲区的末尾:
在这里插入图片描述
11.1.2 AOF文件的写入与同步

Redis的服务器进程是一个事件循环(loop),这个循环中的文件事件(文件描述符事件?)负责接收客户端的命令请求、向客户端发送命令回复,而时间事件则负责执行一个需要定时运行的函数。

因为服务器在处理文件事件时可能会执行写命令,使得一些内容被追加到aof_buf缓冲区里,所以在服务器每次结束一个事件循环前,都会调用flushAppendOnlyFile函数,考虑是否需要将aof_buf缓冲区中的内容写入AOF文件里,事件循环可用以下伪代码表示:

def eventLoop():while True:# 处理文件事件,接收命令请求以及发送命令回复# 处理命令请求时可能会有新内容加入aof_buf缓冲区中processFileEvents()# 处理时间事件processTimeEvents()# 考虑是否要将aof_buf中的内容写入AOF文件flushAppendOnlyFile()

flushAppendOnlyFile函数的行为由服务器配置中的appendfsync选项决定,不同值产生的行为见下表:
在这里插入图片描述
上图中的写入指的是write操作,同步指的是fsync操作,前者只会将要写的内容加到写队列,而后者会确保设备报告写入完成。

appendfsync选项的默认值是everysec。

为了提高文件的写入效率,现代操作系统中,当用户调用write写文件时,操作系统通常会将要写入的数据保存在内存缓冲区里(内核里),等缓冲区空间满或超过了指定时间后,才真正将缓冲区中的数据写入磁盘里。

这种做法虽然提高了效率,但也带来了安全问题,如果计算机发生停机,那么保存在内存中的写入数据将丢失。

为此,系统提供了fsync和fdatasync同步函数,它们可以强制让操作系统立即将缓冲区中的数据写入硬盘里,从而确保了写入数据的安全性。fsync函数会同步数据和元数据,而fdatasync只会同步数据和必要的元数据(必要的元数据是文件大小这种能确保数据完整性的元数据,而修改时间等不涉及数据完整性的元数据不会被同步)。

例如,服务器在处理文件事件期间执行了三个写入命令:
在这里插入图片描述
那么aof_buf缓冲区中将包含这三条命令的协议内容:
在这里插入图片描述
如果这时flushAppendOnlyFile函数被调用,假设appendfsync选项值为everysec,且距离上次同步AOF文件已经超过一秒,那么服务器会先将aof_buf中的内容写入到AOF文件中,然后再对AOF文件进行同步。

appendfsync选项的值直接决定AOF持久化功能的效率和安全性:
1.当appendfsync值为always时,服务器在每个事件循环都会将aof_buf缓冲中的内容写入并同步到AOF文件,所以always的效率是最慢的,但安全性是最高的。即使出现故障停机,AOF持久化最多只丢失一个事件循环的命令。

2.当appendfsync值为everysec时,服务器在每个事件循环都会将aof_buf缓冲中的内容写入到AOF文件,且每隔一秒就在子线程中对AOF文件进行一次同步,所以everysec的效率足够快。即使出现故障停机,AOF持久化最多只丢失1秒钟的命令。

3.当appendfsync值为no时,服务器在每个事件循环都会将aof_buf缓冲中的内容写入到AOF文件,同步操作由操作系统控制,所以no的效率是最快的。出现故障停机时,将丢失上次同步之后的所有命令。

从平摊操作的角度看,no和everysec模式效率相当。

11.2 AOF文件的载入与数据还原

因为AOF文件里包含了重建数据库状态所需的所有写命令,所以服务器只要读入并重新执行一遍AOF文件中的命令,就可以还原服务器关闭前的数据库状态。

Redis读取AOF文件并还原数据库的步骤:
1.创建一个不带网络连接的伪客户端(fake client):因为Redis命令只能在客户端上下文中执行,而载入AOF文件时的命令来源于文件而非网络连接,因此服务器使用一个没有网络连接的伪客户端来执行AOF文件保存的命令,伪客户端执行命令的效果和普通客户端完全一样。

2.从AOF文件中分析并读取一条写命令。

3.使用伪客户端执行被读出的写命令。

4.一直执行步骤2和3,直到AOF文件中所有命令被执行完。
在这里插入图片描述
11.3 AOF重写

因为AOF持久化是通过保存被执行的写命令来记录数据库状态的,所以随着时间流逝,AOF文件中的内容会越来越多,文件也会越来越大,如果不加以控制,体积过大的AOF文件可能对Redis服务器或宿主机造成影响,且AOF文件越大,还原所需时间就越多。

例如,客户端执行了以下命令:
在这里插入图片描述
那么为了记录这个list的状态,AOF文件就需要保存六条命令。

为了解决AOF文件体积膨胀,Redis提供了AOF文件重写(rewrite)功能。通过该功能,Redis服务器可以生成一个新AOF文件,其中不包含冗余命令,且与旧AOF文件保存的数据库状态相同,新AOF文件体积也小很多。

11.3.1 AOF文件重写的实现

AOF文件重写实际上并不是通过读取、分析现有AOF文件完成的,而是通过读取服务器当前状态来实现的。

例如,对list键执行了以下命令:
在这里插入图片描述
在这里插入图片描述
这种情况下,AOF文件中会写入六条命令。

如果服务器想用尽量少的命令记录list键的状态,最简单高效的方式不是分析现有AOF文件内容,而是直接从数据库中读取list键的状态,然后用一条RPUSH list “C” “D” “E” “F” "G"命令代替现有AOF文件中的六条命令。

整个重写过程可用以下伪代码表示:

def aof_rewrite(new_aof_file_name):# 创建新AOF文件f = create_file(new_aof_file_name)# 遍历数据库for db in redisServer.db:# 忽略空数据库if db.is_empty(): continue# 写入SELECT命令,指定数据库号码f.write_command("SELECT" + db.id)# 遍历数据库中的所有键for key in db:# 忽略已过期的键if key.is_expired(): continue# 根据键的类型对键进行重写if key.type == String:rewrite_string(key)elif key.type == List:rewrite_list(key)elif key.type == Hash:rewrite_hash(key)elif key.type == Set:rewrite_set(key)elif key.type == SortedSet:rewrite_sorted_set(key)# 如果键带有过期时间,那么过期时间也要被重写if key.have_expire_time():rewrite_expire_time(key)# 写入完毕,关闭文件f.close()def rewrite_string(key):# 使用GET命令获取字符串键的值value = GET(key)# 使用SET命令重写字符串键f.write_command(SET, key, value)def rewrite_list(key):# 使用LRANGE命令获取列表键包含的所有元素item1, item2, ..., itemN = LRANGE(key, 0, -1)# 使用RPUSH命令重写列表键f.write_command(RPUSH, key, item1, item2, ..., itemN)def rewrite_hash(key):# 使用HGETALL命令获取哈希键包含的所有键值对field1, value1, field2, value2, ..., fieldN, valueN = HGETALL(key)# 使用HMSET命令重写哈希键f.write_command(HMSET, key, field1, value1, field2, value2, ..., fieldN, valueN)def rewrite_set(key):# 使用SMEMBERS命令获取集合键包含的所有元素elem1, elem2, ..., elemN = SMEMBERS(key)# 使用SADD命令重写集合键f.write_command(SADD, key, elem1, elem2, ..., elemN)def rewrite_sorted_set(key):# 使用ZRANGE命令获取有序集合键包含的所有元素member1, score1, member2, score2, ..., memberN, scoreN = ZRANGE(key, 0, -1, "WITHSCORES")# 使用ZADD命令重写有序集合键f.write_command(ZADD, key, score1, member1, score2, member2, ..., scoreN, memberN)def rewrite_expire_time(key):# 获取毫秒精度的键过期时间timestamp = get_expire_time_in_unixstamp(key)# 使用PEXPIREAT命令重写键的过期时间f.write_command(PEXPIREAT, key, timestamp)

因为aof_rewrite函数生成的新AOF文件只包含还原当前数据库状态所必须的命令,所以新AOF文件不会浪费任何硬盘空间。

对于图11-3所示的数据库:
在这里插入图片描述
aof_rewrite函数产生的新AOF文件如下:
在这里插入图片描述
实际中,为了避免在执行命令时造成客户端输入缓冲区溢出,重写程序在处理列表、哈希表、集合、有序集合这种可能有多个元素的键时,会先检查键包含的元素数量是否超出了redis.h/REDIS_AOF_REWRITE_ITEMS_PER_CMD常量值,如果超出了,那么重写程序将使用多条命令来记录键的值。

在目前版本(Redis 2.9)中,REDIS_AOF_REWRITE_ITEMS_PER_CMD常量值为64。

11.3.2 AOF后台重写

上面介绍的aof_rewrite函数会进行大量的写入操作,所以调用这个函数的线程将被长时间阻塞,因为Redis使用单个线程来处理命令请求,所以如果服务器直接调用aof_rewrite函数,那么在AOF重写期间,服务器将无法处理客户端发来的命令请求。

Redis不希望AOF重写造成服务器无法处理请求,所以Redis将AOF重写放到子进程里执行,这样可以达成两个目的:
1.子进程进行AOF重写期间,服务器进程(父进程)可以继续处理命令请求。

2.子进程带有服务器进程的数据副本,可以避免使用锁。

但使用子进程进行AOF重写期间,服务器进程还在继续处理命令请求,而新的命令可能会对现有数据库状态进行修改,从而使服务器当前数据库状态和重写后的AOF文件保存的数据库状态不一致。

表11-2展示了一个AOF文件重写的例子,当子进程开始进行AOF重写时,数据库中只有k1一个键,但子进程完成AOF文件重写后,服务器又设置了k2、k3、k4三个键,此时AOF文件中只有一个键,而服务器有四个键:
在这里插入图片描述
为了解决这种数据不一致问题,Redis设置了一个AOF重写缓冲区,这个缓冲区在服务器创建子进程后开始使用,当服务器执行完一个写命令后,它会同时将这个写命令发送给AOF缓冲区和AOF重写缓冲区:
在这里插入图片描述
即,在子进程执行AOF重写期间,服务器进程需要执行三个工作:
1.执行客户端发来的命令。

2.将执行后的写命令追加到AOF缓冲区。

3.将执行后的写命令追加到AOF重写缓冲区。

这样一来可以保证:
1.AOF缓冲区中的内容会定期写入和同步到AOF文件,对现有AOF文件的处理工作会如常进行。

2.从创建子进程开始,服务器执行的所有写命令都会被记录到AOF重写缓冲区里。

当子进程完成AOF重写后,它会向父进程发一个信号,父进程在接到该信号后,会调用一个信号处理函数,并执行以下工作:
1.将AOF重写缓冲区中的内容写入新AOF文件中,这时新AOF文件和服务器当前数据库状态一致。

2.对新AOF文件进行改名,原子地(atomic)覆盖现有AOF文件,完成新旧两个AOF文件的替换。

之后父进程就可以继续像往常一样接受命令请求了。

整个AOF后台重写过程中,只有信号处理函数执行时会对服务器进程(父进程)造成阻塞,其他时候,AOF后台重写都不会阻塞父进程,这将AOF重写对服务器性能造成的影响降到了最低。

表11-3展示了一个AOF文件后台重写的全过程:
在这里插入图片描述
以上就是BGREWRITEAOF命令的实现原理。

11.4 重点回顾

1.AOF文件通过保存所有修改数据库的写命令来记录服务器的数据库状态。

2.AOF文件中的所有命令都以Redis命令请求协议的格式保存。

3.命令请求会先保存到AOF缓冲区里,之后再定期写入并同步到AOF文件。

4.appendfsync选项的不同值对AOF持久化功能的安全性和服务器性能有很大影响。

5.服务器只要载入并重新执行保存在AOF文件中的命令,就可以还原数据库。

6.AOF重写可以产生一个新AOF文件,这个新AOF文件和原AOF文件保存的数据库状态一致,但体积更小。

7.AOF重写是一个有歧义的名字,该功能是通过读取数据库中的键值对来实现的,程序无需对现有AOF文件进行任何读入、分析、写入操作。

8.在执行BGREWRITEAOF命令时,Redis服务器会维护一个AOF重写缓冲区,该缓冲区会在子进程创建新AOF文件期间,记录服务器执行的所有写命令。当子进程完成创建新AOF文件的工作后,服务器会将重写缓冲区中的内容追加到新AOF文件,使得新旧两个AOF文件保存的数据库状态一致。最后,服务器用新的AOF文件替换旧的AOF文件,以此来完成AOF文件重写操作。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com