Redis AOF
上文我们介绍了Redis的主框架,以及两种持久化大概原理。本文我们将从源码角度分析Redis AOF的相关实现。(本文基于的版本为2.4.2)
1. 相关配置项
首先我们看一下redis.conf里的关于AOF的配置选项:
Appendonly(yes,no):是否开启AOF持久化
Appendfilename(log/appendonly.aof):AOF日志文件
Appendfsync(always,everysec,no):AOF日志文件同步的频率,always代表每次写都进行fsync,everysec每秒钟一次,no不主动fsync,由OS自己来完成。
no-appendfsync-on-rewrite(yes,no):进行rewrite时,是否需要fsync
auto-aof-rewrite-percentage(100):当AOF文件增长了这个比例(这里是增加了一倍),则后台rewrite自动运行
auto-aof-rewrite-min-size(64mb):进行后面rewrite要求的最小AOF文件大小。这两个选项共同决定了后面rewrite进程是否到达运行的时机
注:rewrite是指当AOF很大的时候,通过重写内存的数据来删除原来的AOF文件,生成最新的内存数据的AOF日志,即把当前的结果逆转化为相应的操作命令写到AOF文件中。
通过上面的选项我们可以知道redis的三个AOF处理流程:
每次更新操作进行的AOF写操作(涉及同步频率)
Rewrite,当满足auto-aof-rewrite-percentage,auto-aof-rewrite-min-size时后面自动运行rewrite操作。
Rewrite,当收到bgrewriteaof客户端命令时,马上运行后面rewrite操作
注:当某个key过期的时候也会写AOF,其实它跟第一种很类似,这里就不再介绍。下面我们将分别介绍这三个流程。
在redis的较新版本中(不知道从哪个版本开始)增加了两个新的子进程:
REDIS_BIO_CLOSE_FILE,负责所有的close file操作
REDIS_BIO_AOF_FSYNC,负责fsync操作
因为这两个操作都可能会引起阻塞,如果在主线程中完成的话,会影响系统对事件的响应,所以这里统一由相应的线程来完成,每个线程都有一个自己的bio_jobs list,用来保存需要的处理的job任务。其相应的代码在bio.c(线程处理函数为bioProcessBackgroundJobs)里,这两个线程在initServer时创建bioInit()。
注:标准命令格式:如set aaa xiang
*3\r\n$3\r\nset\r\n$3\r\n\aaa\r\n$5\r\n\xiang\r\n
其中*3表示该命令的参数个数,后面的数字表示每个参数的长度。
2. AOF的处理流程
2.1 每次更新操作的AOF写
主要涉及的配置是:Appendfsync,no-appendfsync-on-rewrite。该操作的入口在(redis.c):
void call(redisClient *c) {
dirty = server.dirty; //上次的脏数据个数
c->cmd->proc(c); //执行命令操作,如果该操作是一个更新操作,则server.dirty会增加
dirty = server.dirty-dirty; //此次执行导致的脏数据个数
…
if (server.appendonly && dirty > 0) //有脏数据并且开启了AOF功能
feedAppendOnlyFile(c->cmd,c->db->id,c->argv,c->argc); //将数据保存到server.aofbuf
…
}
我们再来看一下feedAppendOnlyFile的实现
void feedAppendOnlyFile(struct redisCommand…{
if (dictid != server.appendseldb){ //当月操作的db与上一次不一样,所以要重新写一个新的select db命令,当rewrite的时候也会把appendseldb置为-1
buf = sdscatprintf(buf,"*2\r\n$6\r\nSELECT\r\n$%lu\r\n%s\r\n",
(unsigned long)strlen(seldb),seldb);
server.appendseldb = dictid;
}
…
buf = catAppendOnlyGenericCommand(buf,argc,argv); //转换为标准命令格式
server.