元素码农
基础
UML建模
数据结构
算法
设计模式
网络
TCP/IP协议
HTTPS安全机制
WebSocket实时通信
数据库
sqlite
postgresql
clickhouse
后端
rust
go
java
php
mysql
redis
mongodb
etcd
nats
zincsearch
前端
浏览器
javascript
typescript
vue3
react
游戏
unity
unreal
C++
C#
Lua
App
android
ios
flutter
react-native
安全
Web安全
测试
软件测试
自动化测试 - Playwright
人工智能
Python
langChain
langGraph
运维
linux
docker
工具
git
svn
🌞
🌙
目录
▶
Redis核心
▶
数据结构
字符串实现
哈希表实现
列表实现
集合实现
有序集合实现
▶
内存管理
内存分配策略
淘汰算法
▶
持久化
▶
RDB机制
快照生成原理
文件格式解析
▶
AOF机制
命令追加策略
重写过程分析
▶
高可用
▶
主从复制
SYNC原理
增量复制
▶
哨兵机制
故障检测
领导选举
▶
高级特性
▶
事务系统
ACID实现
WATCH原理
▶
Lua脚本
沙盒环境
执行管道
▶
实战问题
▶
缓存问题
缓存雪崩
缓存穿透
缓存击穿
缓存预热
▶
数据一致性
读写一致性
双写一致性
▶
性能优化
大key处理
热点key优化
发布时间:
2025-03-22 10:44
↑
☰
# Redis AOF命令追加策略 ## 引言 AOF(Append Only File)是Redis提供的另一种持久化机制,通过记录服务器执行的所有写命令来记录数据库状态。本文将深入探讨AOF的命令追加策略、实现原理以及性能优化方案。 ## 基本概念 ### 1. AOF文件 AOF文件记录了Redis服务器执行的所有写命令,通过重放这些命令可以还原数据库状态。 ```conf # AOF文件名配置 appendfilename "appendonly.aof" # 开启AOF持久化 appendonly yes ``` ### 2. 同步策略 ```conf # 同步策略配置 appendfsync always # 每个写命令都同步 appendfsync everysec # 每秒同步一次 appendfsync no # 由操作系统决定同步时机 ``` ## 命令追加机制 ### 1. 命令转换 ```c // 命令转换为协议格式 void feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int argc) { sds buf = sdsempty(); robj *tmpargv[3]; /* 如果是切换数据库的命令 */ if (dictid != server.aof_selected_db) { char seldb[64]; snprintf(seldb,sizeof(seldb),"*2\r\n$6\r\nSELECT\r\n$%d\r\n%d\r\n", (int)strlen(sdsfromlonglong(dictid)),dictid); buf = sdscatlen(buf,seldb,strlen(seldb)); server.aof_selected_db = dictid; } /* 将命令转换为RESP格式 */ buf = catAppendOnlyGenericCommand(buf,argc,argv); /* 追加到AOF缓冲区 */ if (server.aof_state == AOF_ON) server.aof_buf = sdscatlen(server.aof_buf,buf,sdslen(buf)); } ``` ### 2. 写入策略 ```c // AOF写入实现 void flushAppendOnlyFile(int force) { ssize_t nwritten; int sync_in_progress = 0; mstime_t latency; if (sdslen(server.aof_buf) == 0) return; if (server.aof_fsync == AOF_FSYNC_EVERYSEC) sync_in_progress = bioPendingJobsOfType(BIO_AOF_FSYNC) != 0; if (server.aof_fsync == AOF_FSYNC_EVERYSEC && !force) { if (sync_in_progress) { if (server.aof_flush_postponed_start == 0) { /* 推迟写入 */ server.aof_flush_postponed_start = server.unixtime; return; } else if (server.unixtime - server.aof_flush_postponed_start < 2) { /* 等待同步完成 */ return; } } } /* 写入文件 */ latencyStartMonitor(latency); nwritten = write(server.aof_fd,server.aof_buf,sdslen(server.aof_buf)); latencyEndMonitor(latency); /* 更新统计信息 */ if (nwritten != (ssize_t)sdslen(server.aof_buf)) { /* 写入失败处理 */ } else { if (server.aof_fsync == AOF_FSYNC_ALWAYS) { latencyStartMonitor(latency); fsync(server.aof_fd); latencyEndMonitor(latency); } else if (server.aof_fsync == AOF_FSYNC_EVERYSEC) { if (!sync_in_progress) aof_background_fsync(server.aof_fd); } } } ``` ### 3. 文件同步 ```c // 后台同步实现 void aof_background_fsync(int fd) { bioCreateBackgroundJob(BIO_AOF_FSYNC,(void*)(long)fd,NULL,NULL); } // 同步线程 void *bioProcessBackgroundJobs(void *arg) { struct bio_job *job; unsigned long type = (unsigned long) arg; sigset_t sigset; /* 设置线程信号屏蔽 */ sigemptyset(&sigset); sigaddset(&sigset, SIGTERM); pthread_sigmask(SIG_BLOCK, &sigset, NULL); pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); /* 处理同步任务 */ while(1) { listNode *ln; /* 获取任务 */ pthread_mutex_lock(&bio_mutex[type]); while(listLength(bio_jobs[type]) == 0) { pthread_cond_wait(&bio_newjob_cond[type],&bio_mutex[type]); } ln = listFirst(bio_jobs[type]); job = ln->value; pthread_mutex_unlock(&bio_mutex[type]); /* 执行同步 */ if (type == BIO_AOF_FSYNC) { fsync((long)job->arg1); } /* 清理任务 */ pthread_mutex_lock(&bio_mutex[type]); listDelNode(bio_jobs[type],ln); bio_pending[type]--; pthread_mutex_unlock(&bio_mutex[type]); } } ``` ## 性能优化 ### 1. 写入优化 ```c // 写入缓冲区配置 aof-buffer-size 1mb // 写入策略优化 no-appendfsync-on-rewrite yes ``` ### 2. 磁盘优化 ```c // 文件同步策略 aof-rewrite-incremental-fsync yes // 文件打开方式 aof-use-rdb-preamble yes ``` ### 3. 内存优化 ```c // AOF缓冲区大小限制 aof-buffer-limit 32mb // AOF重写缓冲区大小 aof-rewrite-buffer-size 64mb ``` ## 故障恢复 ### 1. 文件载入 ```c // AOF文件加载 int loadAppendOnlyFile(char *filename) { struct client *fakeClient; FILE *fp = fopen(filename,"r"); struct redis_stat sb; int old_aof_state = server.aof_state; long loops = 0; off_t valid_up_to = 0; if (fp == NULL) { return C_ERR; } /* 检查文件状态 */ if (redis_fstat(fileno(fp),&sb) == -1) { fclose(fp); return C_ERR; } /* 创建伪客户端 */ fakeClient = createFakeClient(); startLoading(fp); /* 解析并执行命令 */ while(1) { int argc, j; unsigned long len; robj **argv; char buf[128]; sds argsds; struct redisCommand *cmd; /* 解析命令 */ if (fgets(buf,sizeof(buf),fp) == NULL) { if (feof(fp)) break; goto readerr; } if (buf[0] != '*') goto fmterr; if (buf[1] == '\0') goto readerr; argc = atoi(buf+1); if (argc < 1) goto fmterr; /* 执行命令 */ argv = zmalloc(sizeof(robj*)*argc); fakeClient->argc = argc; fakeClient->argv = argv; for (j = 0; j < argc; j++) { if (fgets(buf,sizeof(buf),fp) == NULL) { fakeClient->argc = j; /* Free up to j-1. */ freeFakeClientArgv(fakeClient); goto readerr; } /* 解析参数 */ argv[j] = createObject(OBJ_STRING,sdsnew(buf+1)); if (argv[j] == NULL) goto readerr; } /* 查找命令 */ cmd = lookupCommand(argv[0]->ptr); if (!cmd) { freeFakeClientArgv(fakeClient); goto fmterr; } /* 执行命令 */ cmd->proc(fakeClient); freeFakeClientArgv(fakeClient); } /* 清理资源 */ freeFakeClient(fakeClient); fclose(fp); stopLoading(); aofUpdateCurrentSize(); server.aof_state = old_aof_state; return C_OK; } ``` ### 2. 错误处理 ```c // 文件检查工具 redis-check-aof appendonly.aof // 自动修复 redis-check-aof --fix appendonly.aof ``` ## 最佳实践 ### 1. 同步策略选择 - 对数据安全性要求高的场景使用always - 一般场景使用everysec - 注重性能的场景使用no ### 2. 写入优化 - 合理设置缓冲区大小 - 避免频繁fsync - 使用no-appendfsync-on-rewrite ### 3. 监控和维护 ```redis # 监控AOF状态 INFO persistence # 查看AOF文件大小 INFO stats | grep aof_current_size ``` ### 4. 安全考虑 - AOF文件访问权限控制 - 定期备份AOF文件 - 配置AOF重写阈值 ## 常见问题 ### 1. 性能影响 - 写入延迟 - 磁盘IO开销 - 文件大小增长 ### 2. 数据一致性 - 部分写入问题 - 命令丢失风险 - 重写期间的数据同步 ### 3. 运维挑战 - 文件体积管理 - 重写策略调优 - 灾难恢复流程 ## 总结 AOF命令追加策略是Redis持久化的重要组成部分,通过记录写命令来保证数据的可靠性。在实际应用中,需要根据业务场景选择合适的同步策略,并通过合理的配置和优化来平衡性能和可靠性。同时,建立完善的监控和维护机制,确保AOF机制的稳定运行。