前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >redis.conf之save配置项解读

redis.conf之save配置项解读

作者头像
一见
发布2019-03-14 15:18:00
1.6K0
发布2019-03-14 15:18:00
举报
文章被收录于专栏:蓝天蓝天

配置示例:

代码语言:javascript
复制
save 900 1
save 300 10
save 60 3600

配置解读:

1) “save 900 1”表示如果超过每1秒有900个key发生变化(新增、修改和删除),则重写rdb文件;

2) “save 300 10”表示如果每10秒有300个key发生变化(新增、修改和删除),则重写rdb文件;

3) “save 60 3600”表示如果每3600秒有900个key发生变化(新增、修改和删除),则重写rdb文件。

作用:

控制什么时候生成rdb文件(快照,也可叫Checkpoint,即检查点)。

进程启动的时候,会将每一行save读进到类型为struct saveparam的数组中。这个没有排序,依在redis.conf中的先后顺序。在检查时,只要满足就不会再检查下一条规则。

配置策略:

如果同时开启了aof,则可考虑将save的参数调大一点,以减少写rdb带来的压力。实际上如果开启了aof,redis在启动时只会读取aof文件,而不会读取rdb文件:

代码语言:javascript
复制
// Function called at startup to load RDB or AOF file in memory.
void loadDataFromDisk(void) {
if (server.aof_state == AOF_ON) {
// 允许空的aof文件,
// 如果读取aof文件出错,则调用exit(1)直接退出进程
if (loadAppendOnlyFile(server.aof_filename) == C_OK)
serverLog(LL_NOTICE,"DB loaded from append only file: %.3f seconds",(float)(ustime()-start)/1000000);
} else {
if (rdbLoad(server.rdb_filename,&rsi) == C_OK) {
}
}
// #define AOF_OFF 0             /* AOF is off */
// #define AOF_ON 1              /* AOF is on */
void loadServerConfigFromString(char *config) {
。。。。。。
} else if (!strcasecmp(argv[0],"appendonly") && argc == 2) {
int yes;
if ((yes = yesnotoi(argv[1])) == -1) {
err = "argument must be 'yes' or 'no'"; goto loaderr;
}
server.aof_state = yes ? AOF_ON : AOF_OFF;
}
。。。。。。
}
int yesnotoi(char *s) {
if (!strcasecmp(s,"yes")) return 1;
else if (!strcasecmp(s,"no")) return 0;
else return -1;
}

调用顺序:

main()/server.c ->

aeMain()/ae.c -> while (!stop) { aeProcessEvents()/ae.c } ->

serverCron()/server.c -> rdbSaveBackground()/server.c

注:

aeProcessEvents可看作是个epoll_wait调用,在Linux上实际正是epoll_wait调用,而在Solaris上则是port_getn调用。

相关源代码:

代码语言:javascript
复制
int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
。。。。。。
// Check if a background saving or AOF rewrite in progress terminated.
// 如果已有rdb和aof进程,检查进程是否已退出。
// 如果已退出,则会善后处理,否则什么也不做,等待下一次循环时再次检查
if (server.rdb_child_pid != -1 ||
server.aof_child_pid != -1 ||
ldbPendingChildren()) {
int statloc;
pid_t pid;
if ((pid = wait3(&statloc,WNOHANG,NULL)) != 0) {
int exitcode = WEXITSTATUS(statloc);
。。。。。。
}
} else {
// If there is not a background saving/rewrite
// in progress check if we have to save/rewrite now.
// 按在redis.conf中定义的顺序依次遍历每一行配置项
// 最终是否进行写rdb操作(即生成快照文件),不仅由redis.conf
// 中的配置项决定,还要看上一次操作的结果和状态。
for (j = 0; j < server.saveparamslen; j++) {
struct saveparam *sp = server.saveparams+j;
// Save if we reached the given amount of changes,
// the given amount of seconds, and if the latest bgsave was
// successful or if, in case of an error, at least
// CONFIG_BGSAVE_RETRY_DELAY seconds already elapsed.
// CONFIG_BGSAVE_RETRY_DELAY(5): Wait a few secs before trying again.
if (server.dirty >= sp->changes &&
server.unixtime-server.lastsave > sp->seconds &&
(server.unixtime-server.lastbgsave_try>CONFIG_BGSAVE_RETRY_DELAY ||
server.lastbgsave_status == C_OK)) {
serverLog(LL_NOTICE,"%d changes in %d seconds. Saving...",
sp->changes, (int)sp->seconds);
rdbSaveInfo rsi, *rsiptr;
rsiptr = rdbPopulateSaveInfo(&rsi);
rdbSaveBackground(server.rdb_filename,rsiptr);
break; // 遇到一条满足的即结束处理,因为已没有必要判断是否满足下一条配置规则
}
}
}
。。。。。。
}
// rdb.c
int rdbSaveBackground(char *filename, rdbSaveInfo *rsi) {
。。。。。。
server.lastbgsave_try = time(NULL);
。。。。。。
// 创建写rdb的子进程
if ((childpid = fork()) == 0) {
redisSetProcTitle("redis-rdb-bgsave");
retval = rdbSave(filename,rsi);
}
。。。。。。
}
/* Save the DB on disk. Return C_ERR on error, C_OK on success. */
// rdb.c
// rdbSave调用rdbSaveRio将数据写入到rdb文件中
int rdbSave(char *filename, rdbSaveInfo *rsi) {
。。。。。。
// 写rdb文件
if (rdbSaveRio(&rdb,&error,RDB_SAVE_NONE,rsi) == C_ERR) {
errno = error;
goto werr;
}
。。。。。。
serverLog(LL_NOTICE,"DB saved on disk");
server.dirty = 0;
server.lastsave = time(NULL);
server.lastbgsave_status = C_OK;
return C_OK;
}
/* Produces a dump of the database in RDB format sending it to the specified
* Redis I/O channel. On success C_OK is returned, otherwise C_ERR
* is returned and part of the output, or all the output, can be
* missing because of I/O errors.
*
* When the function returns C_ERR and if 'error' is not NULL, the
* integer pointed by 'error' is set to the value of errno just after the I/O
* error. */
int rdbSaveRio(rio *rdb, int *error, int flags, rdbSaveInfo *rsi) {
for (j = 0; j < server.dbnum; j++) {
。。。。。。
// Iterate this DB writing every entry
while((de = dictNext(di)) != NULL) {
。。。。。。
// 将一对对KV写入到rdb文件
if (rdbSaveKeyValuePair(rdb,&key,o,expire) == -1) goto werr;
。。。。。。
}
}
。。。。。。
werr:
if (error) *error = errno;
if (di) dictReleaseIterator(di);
return C_ERR;
}
// 以SADD命令为例,所有写操作,均会修改dirty 的值
void saddCommand(client *c) {
。。。。。。
for (j = 2; j < c->argc; j++) {
if (setTypeAdd(set,c->argv[j]->ptr)) added++;
}
。。。。。。
server.dirty += added;
addReplyLongLong(c,added);
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-01-07 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档