【redis源码学习】持久化机制(2):AOF

命令传播、

刷新AOF缓冲区、

同步磁盘

让我们一个一个来看:


命令传播

feedAppendOnlyFile 函数负责将命令传播到 AOF 缓冲区:

void feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int argc) {

sds buf = sdsempty();

robj *tmpargv[3];

/* The DB this command was targeting is not the same as the last command

  • we appended. To issue a SELECT command is needed. */

if (dictid != server.aof_selected_db) {

char seldb[64];

snprintf(seldb,sizeof(seldb),“%d”,dictid);

buf = sdscatprintf(buf,“*2\r\nKaTeX parse error: Undefined control sequence: \nSELECT at position 4: 6\r\̲n̲S̲E̲L̲E̲C̲T̲\r\n%lu\r\n%s\r\n”,

(unsigned long)strlen(seldb),seldb);

server.aof_selected_db = dictid;

}

if (cmd->proc == expireCommand || cmd->proc == pexpireCommand ||

cmd->proc == expireatCommand) {

/* Translate EXPIRE/PEXPIRE/EXPIREAT into PEXPIREAT */

buf = catAppendOnlyExpireAtCommand(buf,cmd,argv[1],argv[2]);

} else if (cmd->proc == setexCommand || cmd->proc == psetexCommand) {

/* Translate SETEX/PSETEX to SET and PEXPIREAT */

tmpargv[0] = createStringObject(“SET”,3);

tmpargv[1] = argv[1];

tmpargv[2] = argv[3];

buf = catAppendOnlyGenericCommand(buf,3,tmpargv);

decrRefCount(tmpargv[0]);

buf = catAppendOnlyExpireAtCommand(buf,cmd,argv[1],argv[2]);

} else if (cmd->proc == setCommand && argc > 3) {

int i;

robj *exarg = NULL, *pxarg = NULL;

for (i = 3; i < argc; i ++) {

if (!strcasecmp(argv[i]->ptr, “ex”)) exarg = argv[i+1];

if (!strcasecmp(argv[i]->ptr, “px”)) pxarg = argv[i+1];

}

serverAssert(!(exarg && pxarg));

if (exarg || pxarg) {

/* Translate SET [EX seconds][PX milliseconds] to SET and PEXPIREAT */

buf = catAppendOnlyGenericCommand(buf,3,argv);

if (exarg)

buf = catAppendOnlyExpireAtCommand(buf,server.expireCommand,argv[1],

exarg);

if (pxarg)

buf = catAppendOnlyExpireAtCommand(buf,server.pexpireCommand,argv[1],

pxarg);

} else {

buf = catAppendOnlyGenericCommand(buf,argc,argv);

}

} else {

/* All the other commands don’t need translation or need the

  • same translation already operated in the command vector

  • for the replication itself. */

buf = catAppendOnlyGenericCommand(buf,argc,argv);

}

/* Append to the AOF buffer. This will be flushed on disk just before

  • of re-entering the event loop, so before the client will get a

  • positive reply about the operation performed. */

if (server.aof_state == AOF_ON)

server.aof_buf = sdscatlen(server.aof_buf,buf,sdslen(buf));

/* If a background append only file rewriting is in progress we want to

  • accumulate the differences between the child DB and the current one

  • in a buffer, so that when the child process will do its work we

  • can append the differences to the new append only file. */

if (server.aof_child_pid != -1)

aofRewriteBufferAppend((unsigned char*)buf,sdslen(buf));

sdsfree(buf);

}

对于一些带有过期时间的命令,需要进行转换。

对于其他命令,直接写入buf暂存区。

如果服务器开启AOF功能,则将buf暂存区内容写入AOF缓冲区。

如果当前存在AOF进程执行AOF重写操作,则将buf暂存区的数据写入AOF重写缓冲区。


刷新缓冲区 && 同步磁盘

void flushAppendOnlyFile(int force) {

ssize_t nwritten;

int sync_in_progress = 0;

mstime_t latency;

if (sdslen(server.aof_buf) == 0) {

/* Check if we need to do fsync even the aof buffer is empty,

  • because previously in AOF_FSYNC_EVERYSEC mode, fsync is

  • called only when aof buffer is not empty, so if users

  • stop write commands before fsync called in one second,

  • the data in page cache cannot be flushed in time. */

if (server.aof_fsync == AOF_FSYNC_EVERYSEC &&

server.aof_fsync_offset != server.aof_current_size &&

server.unixtime > server.aof_last_fsync &&

!(sync_in_progress = aofFsyncInProgress())) {

goto try_fsync;

} else {

return;

}

}

//检查当前是否存在后台线程正在同步磁盘

if (server.aof_fsync == AOF_FSYNC_EVERYSEC)

sync_in_progress = aofFsyncInProgress();

//如果存在,如果磁盘同步策略为每秒同步,则延迟该AOF缓冲区刷新操作,退出函数;

//如果已延迟时间超过2s,则强制刷新AOF缓冲区,函数继续向下执行。

if (server.aof_fsync == AOF_FSYNC_EVERYSEC && !force) {

/* With this append fsync policy we do background fsyncing.

  • If the fsync is still in progress we can try to delay

  • the write for a couple of seconds. */

if (sync_in_progress) {

if (server.aof_flush_postponed_start == 0) {

/* No previous write postponing, remember that we are

  • postponing the flush and return. */

server.aof_flush_postponed_start = server.unixtime;

return;

} else if (server.unixtime - server.aof_flush_postponed_start < 2) {

/* We were already waiting for fsync to finish, but for less

  • than two seconds this is still ok. Postpone again. */

return;

}

/* Otherwise fall trough, and go write since we can’t wait

  • over two seconds. */

server.aof_delayed_fsync++;

serverLog(LL_NOTICE,“Asynchronous AOF fsync is taking too long (disk is busy?). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis.”);

}

}

/* We want to perform a single write. This should be guaranteed atomic

  • at least if the filesystem we are writing is a real physical one.

  • While this will save us against the server being killed I don’t think

  • there is much to do about the whole server stopping for power problems

  • or alike */

if (server.aof_flush_sleep && sdslen(server.aof_buf)) {

usleep(server.aof_flush_sleep);

}

latencyStartMonitor(latency);

nwritten = aofWrite(server.aof_fd,server.aof_buf,sdslen(server.aof_buf));

latencyEndMonitor(latency);

/* We want to capture different events for delayed writes:

  • when the delay happens with a pending fsync, or with a saving child

  • active, and when the above two conditions are missing.

  • We also use an additional event name to save all samples which is

  • useful for graphing / monitoring purposes. */

if (sync_in_progress) {

latencyAddSampleIfNeeded(“aof-write-pending-fsync”,latency);

} else if (hasActiveChildProcess()) {

latencyAddSampleIfNeeded(“aof-write-active-child”,latency);

} else {

latencyAddSampleIfNeeded(“aof-write-alone”,latency);

}

latencyAddSampleIfNeeded(“aof-write”,latency);

/* We performed the write so reset the postponed flush sentinel to zero. */

server.aof_flush_postponed_start = 0;

if (nwritten != (ssize_t)sdslen(server.aof_buf)) {

static time_t last_write_error_log = 0;

int can_log = 0;

/* Limit logging rate to 1 line per AOF_WRITE_LOG_ERROR_RATE seconds. */

if ((server.unixtime - last_write_error_log) > AOF_WRITE_LOG_ERROR_RATE) {

can_log = 1;

last_write_error_log = server.unixtime;

}

/* Log the AOF write error and record the error code. */

if (nwritten == -1) {

if (can_log) {

serverLog(LL_WARNING,“Error writing to the AOF file: %s”,

strerror(errno));

server.aof_last_write_errno = errno;

}

} else {

if (can_log) {

serverLog(LL_WARNING,"Short write while writing to "

"the AOF file: (nwritten=%lld, "

“expected=%lld)”,

(long long)nwritten,

(long long)sdslen(server.aof_buf));

}

if (ftruncate(server.aof_fd, server.aof_current_size) == -1) {

if (can_log) {

serverLog(LL_WARNING, "Could not remove short write "

"from the append-only file. Redis may refuse "

"to load the AOF the next time it starts. "

“ftruncate: %s”, strerror(errno));

}

} else {

/* If the ftruncate() succeeded we can set nwritten to

  • -1 since there is no longer partial data into the AOF. */

nwritten = -1;

}

server.aof_last_write_errno = ENOSPC;

}

/* Handle the AOF write error. */

if (server.aof_fsync == AOF_FSYNC_ALWAYS) {

/* We can’t recover when the fsync policy is ALWAYS since the

  • reply for the client is already in the output buffers, and we

  • have the contract with the user that on acknowledged write data

  • is synced on disk. */

serverLog(LL_WARNING,“Can’t recover from AOF write error when the AOF fsync policy is ‘always’. Exiting…”);

exit(1);

} else {

/* Recover from failed write leaving data into the buffer. However

  • set an error to stop accepting writes as long as the error

  • condition is not cleared. */

server.aof_last_write_status = C_ERR;

/* Trim the sds buffer if there was a partial write, and there

  • was no way to undo it with ftruncate(2). */

if (nwritten > 0) {

server.aof_current_size += nwritten;

sdsrange(server.aof_buf,nwritten,-1);

}

return; /* We’ll try again on the next call… */

}

} else {

/* Successful write(2). If AOF was in error state, restore the

  • OK state and log the event. */

if (server.aof_last_write_status == C_ERR) {

serverLog(LL_WARNING,

“AOF write error looks solved, Redis can write again.”);

server.aof_last_write_status = C_OK;

}

}

server.aof_current_size += nwritten;

/* Re-use AOF buffer when it is small enough. The maximum comes from the

  • arena size of 4k minus some overhead (but is otherwise arbitrary). */

if ((sdslen(server.aof_buf)+sdsavail(server.aof_buf)) < 4000) {

sdsclear(server.aof_buf);

} else {

sdsfree(server.aof_buf);

server.aof_buf = sdsempty();

}

//下面属于同步磁盘范畴

try_fsync:

/* Don’t fsync if no-appendfsync-on-rewrite is set to yes and there are

  • children doing I/O in the background. */

if (server.aof_no_fsync_on_rewrite && hasActiveChildProcess())

return;

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

言尽于此,完结

无论是一个初级的 coder,高级的程序员,还是顶级的系统架构师,应该都有深刻的领会到设计模式的重要性。

  • 第一,设计模式能让专业人之间交流方便,如下:

程序员A:这里我用了XXX设计模式

程序员B:那我大致了解你程序的设计思路了

  • 第二,易维护

项目经理:今天客户有这样一个需求…

程序员:明白了,这里我使用了XXX设计模式,所以改起来很快

  • 第三,设计模式是编程经验的总结

程序员A:B,你怎么想到要这样去构建你的代码

程序员B:在我学习了XXX设计模式之后,好像自然而然就感觉这样写能避免一些问题

  • 第四,学习设计模式并不是必须的

程序员A:B,你这段代码使用的是XXX设计模式对吗?

程序员B:不好意思,我没有学习过设计模式,但是我的经验告诉我是这样写的

image

从设计思想解读开源框架,一步一步到Spring、Spring5、SpringMVC、MyBatis等源码解读,我都已收集整理全套,篇幅有限,这块只是详细的解说了23种设计模式,整理的文件如下图一览无余!

image

搜集费时费力,能看到此处的都是真爱!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
第二,易维护

项目经理:今天客户有这样一个需求…

程序员:明白了,这里我使用了XXX设计模式,所以改起来很快

  • 第三,设计模式是编程经验的总结

程序员A:B,你怎么想到要这样去构建你的代码

程序员B:在我学习了XXX设计模式之后,好像自然而然就感觉这样写能避免一些问题

  • 第四,学习设计模式并不是必须的

程序员A:B,你这段代码使用的是XXX设计模式对吗?

程序员B:不好意思,我没有学习过设计模式,但是我的经验告诉我是这样写的

[外链图片转存中…(img-smBkDZAT-1713535241732)]

从设计思想解读开源框架,一步一步到Spring、Spring5、SpringMVC、MyBatis等源码解读,我都已收集整理全套,篇幅有限,这块只是详细的解说了23种设计模式,整理的文件如下图一览无余!

[外链图片转存中…(img-G01fvrYh-1713535241734)]

搜集费时费力,能看到此处的都是真爱!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 25
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值