Redis之AOF重写及其实现原理

Reference: https://blog.csdn.net/hezhiqiang1314/article/details/69396887

AOF 重写
AOF 持久化是通过保存被执行的写命令来记录数据库状态的,所以AOF文件的大小随着时间的流逝一定会越来越大;影响包括但不限于:对于Redis服务器,计算机的存储压力;AOF还原出数据库状态的时间增加;
为了解决AOF文件体积膨胀的问题,Redis提供了AOF重写功能:Redis服务器可以创建一个新的AOF文件来替代现有的AOF文件,新旧两个文件所保存的数据库状态是相同的,但是新的AOF文件不会包含任何浪费空间的冗余命令,通常体积会较旧AOF文件小很多。
AOF 文件重写的实现
AOF重写并不需要对原有AOF文件进行任何的读取,写入,分析等操作,这个功能是通过读取服务器当前的数据库状态来实现的。
# 假设服务器对键list执行了以下命令s;
127.0.0.1:6379> RPUSH list "A" "B"
(integer) 2
127.0.0.1:6379> RPUSH list "C"
(integer) 3
127.0.0.1:6379> RPUSH list "D" "E"
(integer) 5
127.0.0.1:6379> LPOP list
"A"
127.0.0.1:6379> LPOP list
"B"
127.0.0.1:6379> RPUSH list "F" "G"
(integer) 5
127.0.0.1:6379> LRANGE list 0 -1
1) "C"
2) "D"
3) "E"
4) "F"
5) "G"
127.0.0.1:6379>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
当前列表键list在数据库中的值就为["C", "D", "E", "F", "G"]。要使用尽量少的命令来记录list键的状态,最简单的方式不是去读取和分析现有AOF文件的内容,,而是直接读取list键在数据库中的当前值,然后用一条RPUSH list "C" "D" "E" "F" "G"代替前面的6条命令。
AOF重写功能的实现原理
首先从数据库中读取键现在的值,然后用一条命令去记录键值对,代替之前记录该键值对的多个命令;
伪代码表示如下;
def AOF_REWRITE(tmp_tile_name):

f = create(tmp_tile_name)

# 遍历所有数据库
for db in redisServer.db:

# 如果数据库为空,那么跳过这个数据库
if db.is_empty(): continue

# 写入 SELECT 命令,用于切换数据库
f.write_command("SELECT " + db.number)

# 遍历所有键
for key in db:

# 如果键带有过期时间,并且已经过期,那么跳过这个键
if key.have_expire_time() and key.is_expired(): continue

if key.type == String:

# 用 SET key value 命令来保存字符串键

value = get_value_from_string(key)

f.write_command("SET " + key + value)

elif key.type == List:

# 用 RPUSH key item1 item2 ... itemN 命令来保存列表键

item1, item2, ..., itemN = get_item_from_list(key)

f.write_command("RPUSH " + key + item1 + item2 + ... + itemN)

elif key.type == Set:

# 用 SADD key member1 member2 ... memberN 命令来保存集合键

member1, member2, ..., memberN = get_member_from_set(key)

f.write_command("SADD " + key + member1 + member2 + ... + memberN)

elif key.type == Hash:

# 用 HMSET key field1 value1 field2 value2 ... fieldN valueN 命令来保存哈希键

field1, value1, field2, value2, ..., fieldN, valueN =\
get_field_and_value_from_hash(key)

f.write_command("HMSET " + key + field1 + value1 + field2 + value2 +\
... + fieldN + valueN)

elif key.type == SortedSet:

# 用 ZADD key score1 member1 score2 member2 ... scoreN memberN
# 命令来保存有序集键

score1, member1, score2, member2, ..., scoreN, memberN = \
get_score_and_member_from_sorted_set(key)

f.write_command("ZADD " + key + score1 + member1 + score2 + member2 +\
... + scoreN + memberN)

else:

raise_type_error()

# 如果键带有过期时间,那么用 EXPIREAT key time 命令来保存键的过期时间
if key.have_expire_time():
f.write_command("EXPIREAT " + key + key.expire_time_in_unix_timestamp())

# 关闭文件
f.close()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
实际为了避免执行命令时造成客户端输入缓冲区溢出,重写程序在处理list hash set zset时,会检查键所包含的元素的个数,如果元素的数量超过了redis.h/REDIS_AOF_REWRITE_ITEMS_PER_CMD常量的值,那么重写程序会使用多条命令来记录键的值,而不是单使用一条命令。该常量默认值是64– 即每条命令设置的元素的个数 是最多64个,使用多条命令重写实现集合键中元素数量超过64个的键;
AOF后台重写
aof_rewrite函数可以创建新的AOF文件,但是这个函数会进行大量的写入操作,所以调用这个函数的线程将被长时间的阻塞,因为Redis服务器使用单线程来处理命令请求;所以如果直接是服务器进程调用AOF_REWRITE函数的话,那么重写AOF期间,服务器将无法处理客户端发送来的命令请求;
Redis不希望AOF重写会造成服务器无法处理请求,所以Redis决定将AOF重写程序放到子进程(后台)里执行。这样处理的最大好处是:
子进程进行AOF重写期间,主进程可以继续处理命令请求;
子进程带有主进程的数据副本,使用子进程而不是线程,可以避免在锁的情况下,保证数据的安全性。
使用子进程进行AOF重写的问题
子进程在进行AOF重写期间,服务器进程还要继续处理命令请求,而新的命令可能对现有的数据进行修改,这会让当前数据库的数据和重写后的AOF文件中的数据不一致。
如何修正
为了解决这种数据不一致的问题,Redis增加了一个AOF重写缓存,这个缓存在fork出子进程之后开始启用,Redis服务器主进程在执行完写命令之后,会同时将这个写命令追加到AOF缓冲区和AOF重写缓冲区
即子进程在执行AOF重写时,主进程需要执行以下三个工作:
执行client发来的命令请求;
将写命令追加到现有的AOF文件中;
将写命令追加到AOF重写缓存中。

效果
可以保证:
AOF缓冲区的内容会定期被写入和同步到AOF文件中,对现有的AOF文件的处理工作会正常进行
从创建子进程开始,服务器执行的所有写操作都会被记录到AOF重写缓冲区中;
完成AOF重写之后
当子进程完成对AOF文件重写之后,它会向父进程发送一个完成信号,父进程接到该完成信号之后,会调用一个信号处理函数,该函数完成以下工作:

将AOF重写缓存中的内容全部写入到新的AOF文件中;这个时候新的AOF文件所保存的数据库状态和服务器当前的数据库状态一致;
对新的AOF文件进行改名,原子的覆盖原有的AOF文件;完成新旧两个AOF文件的替换。
当这个信号处理函数执行完毕之后,主进程就可以继续像往常一样接收命令请求了。在整个AOF后台重写过程中,只有最后的“主进程写入命令到AOF缓存”和“对新的AOF文件进行改名,覆盖原有的AOF文件。”这两个步骤(信号处理函数执行期间)会造成主进程阻塞,在其他时候,AOF后台重写都不会对主进程造成阻塞,这将AOF重写对性能造成的影响降到最低。

以上,即AOF后台重写,也就是BGREWRITEAOF命令的工作原理。

触发AOF后台重写的条件
AOF重写可以由用户通过调用BGREWRITEAOF手动触发。
服务器在AOF功能开启的情况下,会维持以下三个变量:

记录当前AOF文件大小的变量aof_current_size。
记录最后一次AOF重写之后,AOF文件大小的变量aof_rewrite_base_size。
增长百分比变量aof_rewrite_perc。
每次当serverCron(服务器周期性操作函数)函数执行时,它会检查以下条件是否全部满足,如果全部满足的话,就触发自动的AOF重写操作:

没有BGSAVE命令(RDB持久化)/AOF持久化在执行;
没有BGREWRITEAOF在进行;
当前AOF文件大小要大于server.aof_rewrite_min_size(默认为1MB),或者在redis.conf配置了auto-aof-rewrite-min-size大小;
当前AOF文件大小和最后一次重写后的大小之间的比率等于或者等于指定的增长百分比(在配置文件设置了auto-aof-rewrite-percentage参数,不设置默认为100%)
如果前面三个条件都满足,并且当前AOF文件大小比最后一次AOF重写时的大小要大于指定的百分比,那么触发自动AOF重写。

总结
AOF重写的目的是为了解决AOF文件体积膨胀的问题,使用更小的体积来保存数据库状态,整个重写过程基本上不影响Redis主进程处理命令请求;
AOF重写其实是一个有歧义的名字,实际上重写工作是针对数据库的当前状态来进行的,重写过程中不会读写、也不适用原来的AOF文件;
AOF可以由用户手动触发,也可以由服务器自动触发。
ref:
blog1
Redis的设计与实现
---------------------
作者:hezhiqiang1314
来源:CSDN
原文:https://blog.csdn.net/hezhiqiang1314/article/details/69396887
版权声明:本文为博主原创文章,转载请附上博文链接!

原文地址:https://www.cnblogs.com/skying555/p/10355975.html

时间: 2024-10-07 19:30:40

Redis之AOF重写及其实现原理的相关文章

由redis持久化AOF重写想到Excel保存过程

redis的持久机制有两种,一种是snapshot,另一种是AOF重写.第一种机制是按事先定制的策略,周期性地异步或同步将数据从内存同步至磁盘,在客户端的操作是由SAVE或BGSAVE命令执行.而AOF重写则是记录写操作至指定的文件尾部实现持久化.AOF重写过程如下: (1) redis主进程通过fork创建子进程 (2)子进程根据当前redis内存中的数据生成数据库重建命令序列到临时文件中 (3)父进程继承client的新请求,把请求中的写操作继续追加至原来的AOF文件(而不是直接写入临  

Redis持久化——AOF(二)

核心知识点: 1.AOF:以独立日志的方式记录写命令,重启时再执行命令.与RDB不同的是解决数据持久化的实时性,可以记录所有写操作. 2.AOF工作流程:写入命令.文件同步.文件重写.文件加载. 3.命令写入 a.将命令以文本协议格式保存在缓存中. b.为什么使用文本协议格式?兼容性.避免二次开销.可读性. c.为什么写入到缓存?这样不会受制于磁盘的IO性能 4.文件同步:从内存同步到文件中,有三种机制,使用的系统命令是write或fsync. (1).write会触发写延迟,依赖系统的调度机制

第五章:Redis持久化-AOF持久化

AOF持久化 AOF全称append only file持久化:以独立日志的方式记录每次写命令,重启时再重新执行AOF文件中的命令达到恢复数据的目的: AOF主要作用是解决了数据实时持久化的问题: 使用AOF 开始AOF需要设置appendonly yes,默认不开启. AOF文件名通过appendonlyname配置,默认文件名为appendonly.aof: AOF工作流程操作:命令写入(append).文件同步(sync).文件重写(rewrite).重启加载(reload): 所有写入命

Redis持久化--AOF

除了RDB持久化之外,Redis还提供了AOF(Append Only File)持久化功能.与RDB持久化通过保存数据库中键值对来保存数据库的状态不同,AOF持久化是通过保存Redis服务器所执行的写命令来记录数据库的状态.被写入AOF文件的所有命令都是以Redis的命令请求协议格式保存的,该格式是一种纯本文的格式,所以可以通过直接打开AOF文件,观察里面的类容. 1 AOF持久化的实现 AOF持久化功能的实现可以分为:命令追加(append),文件写入(write),文件同步(sync)三个

redis 持久化 AOF RDB

Redis的AOF持久化策略是将发送到redis服务端的每一条命令都记录下来,并且保存到硬盘中的AOF文件中,类似打日志文件,来一条命令就记录一条. AOF设置 AOF文件的位置和RDB文件的位置相同,都是通过dir参数设置,默认的文件名是appendonly.aof,可以通过appendfilename参数来修改. AOF测试 当客户端向服务器发送一些redis命令时,Redis会将所执行的命令记录到aof文件中,如下所示: 当redis服务器重启后,会将执行该aof文件,达到数据恢复的目的.

浅谈Redis五种数据结构的底层原理

概念 Redis作为一个开源的用C编写的非关系型数据库,基于优秀的CRUD效率,常用于软件系统的缓存,其本身提供了以下五种数据格式: string:字符串 list:列表 hash:散列表 set:无序集合 zset:有序集合 接下来我们就要针对这五种数据结构,来分析其底层的结构 这里选用的版本是redis-5.0.4,所以可能有很多地方和如今网络上的其他博文不太一致,不同的地方我会在文中指出 string 因为redis使用c语言开发,所以自然没有java和c++的那些字符串类库,在redis

Redis之-AOF

AOF是基于日志的,类似于Oracle的redo,Mysql的binlog日志. Aof 的配置appendonly no # 是否打开 aof日志功能 appendfsync always # 每1个命令,都立即同步到aof. 安全,速度慢appendfsync everysec # 折衷方案,每秒写1次appendfsync no # 写入工作交给操作系统,由操作系统判断缓冲区大小,统一写入到aof. 同步频率低,速度快, no-appendfsync-on-rewrite yes: # 正

Redis的AOF与RDB

AOF持久化的实现 AOF持久化的实现可以分为命令追加.文件写入和文件同步三个步骤. struct redisServer { sds aof_buf;/* AOF buffer, written before entering the event loop */ } AOF持久化功能处于打开状态时,服务器在执行完一个写命令之后,会以协议格式将被执行的写命令追加到服务器状态的aof_buf缓冲区的末尾. AOF重写 Redis提供了AOF重写功能:Redis服务器可以创建一个新的AOF文件来替代

Redis的AOF功能

引言:  Redis是基于内存的数据库,同时也提供了若干持久化的方案,允许用户把内存中的数据,写入本地文件系统,以备下次重启或者当机之后继续使用.本文将描述如何基于Redis来设置AOF功能 什么是Redis的AOF? AOF是AppendOnly File的缩写,是Redis系统提供了一种记录Redis操作的持久化方案,在AOF生成的文件中,将忠实记录发生在Redis的操作,从而达到在Redis服务器重启或者当机之后,继续恢复之前数据状态的机制. 以下我们来简要看看如何在Redis中使用AOF