Redis 데이터 지구 화 메커니즘 AOF 원리 분석 1
Redis AOF 데이터 지구 화 메커니즘 의 실현 관련 코드 는 redis. c, redis. h, aof. c, bio. c, rio. c, config. c 이다.
본문 을 읽 기 전에 먼저 Redis 데이터 지구 화 메커니즘 AOF 원리 분석의 설정 을 읽 고 AOF 관련 매개 변수의 해석, 문장 링크 를 이해 하 십시오.
http://blog.csdn.net/acceptedxukai/article/details/18135219
전재 하 다http://blog.csdn.net/acceptedxukai/article/details/18136903
다음은 AOF 데이터 지구 화 메커니즘 의 실현 을 소개 한다.
서버 시작 AOF 파일 데이터 불 러 오기
서버 에서 AOF 파일 데 이 터 를 불 러 오 는 실행 절 차 는 main () - > initServerConfig () - > loadServerConfig () - > initServer () - > loadDataFromDisk () 입 니 다.initServerConfig () 는 기본 AOF 매개 변수 설정 을 초기 화 합 니 다.loadserverConfig () 설정 파일 redis. conf 에서 AOF 의 매개 변수 설정 을 불 러 옵 니 다. 서버 의 기본 AOF 매개 변수 설정 을 덮어 씁 니 다. appendonly on 을 설정 하면 AOF 데이터 영구 화 기능 이 활성 화 됩 니 다. server. aofstate 인자 가 REDIS 로 설정 됨AOF_ON;loadDataFromDisk () 판단 server. aofstate == REDIS_AOF_ON, 결 과 는 True 에서 loadAppendOnlyFile 함 수 를 호출 하여 AOF 파일 의 데 이 터 를 불 러 옵 니 다. 불 러 오 는 방법 은 AOF 파일 의 데 이 터 를 읽 는 것 입 니 다. AOF 파일 에 저 장 된 데 이 터 는 클 라 이언 트 가 보 낸 요청 형식 과 같 기 때문에 서버 는 가짜 클 라 이언 트 fakeClient 를 만 듭 니 다.해 석 된 AOF 파일 데 이 터 를 클 라 이언 트 요청 처럼 각종 명령 어, cmd - > proc (fakeClient) 를 호출 하여 AOF 파일 의 데 이 터 를 Redis Server 데이터베이스 에 재현 합 니 다.
/* Function called at startup to load RDB or AOF file in memory. */
void loadDataFromDisk(void) {
long long start = ustime();
if (server.aof_state == REDIS_AOF_ON) {
if (loadAppendOnlyFile(server.aof_filename) == REDIS_OK)
redisLog(REDIS_NOTICE,"DB loaded from append only file: %.3f seconds",(float)(ustime()-start)/1000000);
} else {
if (rdbLoad(server.rdb_filename) == REDIS_OK) {
redisLog(REDIS_NOTICE,"DB loaded from disk: %.3f seconds",
(float)(ustime()-start)/1000000);
} else if (errno != ENOENT) {
redisLog(REDIS_WARNING,"Fatal error loading the DB: %s. Exiting.",strerror(errno));
exit(1);
}
}
}
서버 는 AOF 파일 을 불 러 오 는 것 은 AOF 파일 의 데이터 가 RDB 파일 의 데이터 보다 새 롭 기 때 문 이 라 고 먼저 판단 했다.int loadAppendOnlyFile(char *filename) {
struct redisClient *fakeClient;
FILE *fp = fopen(filename,"r");
struct redis_stat sb;
int old_aof_state = server.aof_state;
long loops = 0;
//redis_fstat fstat64 , fileno(fp) , sb ,
// stat ,st_size
if (fp && redis_fstat(fileno(fp),&sb) != -1 && sb.st_size == 0) {
server.aof_current_size = 0;
fclose(fp);
return REDIS_ERR;
}
if (fp == NULL) {//
redisLog(REDIS_WARNING,"Fatal error: can't open the append log file for reading: %s",strerror(errno));
exit(1);
}
/* Temporarily disable AOF, to prevent EXEC from feeding a MULTI
* to the same file we're about to read. */
server.aof_state = REDIS_AOF_OFF;
fakeClient = createFakeClient(); //
startLoading(fp); // rdb.c ,
while(1) {
int argc, j;
unsigned long len;
robj **argv;
char buf[128];
sds argsds;
struct redisCommand *cmd;
/* Serve the clients from time to time */
// ,ftello() , long
if (!(loops++ % 1000)) {
loadingProgress(ftello(fp));// aof ,ftellno(fp)
aeProcessEvents(server.el, AE_FILE_EVENTS|AE_DONT_WAIT);//
}
// AOF
if (fgets(buf,sizeof(buf),fp) == NULL) {
if (feof(fp))// EOF
break;
else
goto readerr;
}
// AOF , Redis
if (buf[0] != '*') goto fmterr;
argc = atoi(buf+1);//
if (argc < 1) goto fmterr;
argv = zmalloc(sizeof(robj*)*argc);//
for (j = 0; j < argc; j++) {
if (fgets(buf,sizeof(buf),fp) == NULL) goto readerr;
if (buf[0] != '$') goto fmterr;
len = strtol(buf+1,NULL,10);// bulk
argsds = sdsnewlen(NULL,len);// sds
// bulk
if (len && fread(argsds,len,1,fp) == 0) goto fmterr;
argv[j] = createObject(REDIS_STRING,argsds);
if (fread(buf,2,1,fp) == 0) goto fmterr; /* discard CRLF \r
*/
}
/* Command lookup */
cmd = lookupCommand(argv[0]->ptr);
if (!cmd) {
redisLog(REDIS_WARNING,"Unknown command '%s' reading the append only file", (char*)argv[0]->ptr);
exit(1);
}
/* Run the command in the context of a fake client */
fakeClient->argc = argc;
fakeClient->argv = argv;
cmd->proc(fakeClient);//
/* The fake client should not have a reply */
redisAssert(fakeClient->bufpos == 0 && listLength(fakeClient->reply) == 0);
/* The fake client should never get blocked */
redisAssert((fakeClient->flags & REDIS_BLOCKED) == 0);
/* Clean up. Command code may have changed argv/argc so we use the
* argv/argc of the client instead of the local variables. */
for (j = 0; j < fakeClient->argc; j++)
decrRefCount(fakeClient->argv[j]);
zfree(fakeClient->argv);
}
/* This point can only be reached when EOF is reached without errors.
* If the client is in the middle of a MULTI/EXEC, log error and quit. */
if (fakeClient->flags & REDIS_MULTI) goto readerr;
fclose(fp);
freeFakeClient(fakeClient);
server.aof_state = old_aof_state;
stopLoading();
aofUpdateCurrentSize(); // server.aof_current_size,AOF
server.aof_rewrite_base_size = server.aof_current_size;
return REDIS_OK;
…………
}
AOF 매개 변수 설정 에 관 한 블 로그 에 질문 을 남 겼 습 니 다. server. aofcurrent_size 매개 변수 초기 화, 이 의문 을 해결 합 니 다.void aofUpdateCurrentSize(void) {
struct redis_stat sb;
if (redis_fstat(server.aof_fd,&sb) == -1) {
redisLog(REDIS_WARNING,"Unable to obtain the AOF file length. stat: %s",
strerror(errno));
} else {
server.aof_current_size = sb.st_size;
}
}
redis_fstat 는 Linux 에서 fstat 64 함수 의 이름 을 바 꾸 는 것 입 니 다. 이것 은 파일 과 관련 된 매개 변수 정 보 를 가 져 오 는 것 입 니 다. 구체 적 으로 Google, sb. st 를 사용 할 수 있 습 니 다.size 는 현재 AOF 파일 의 크기 입 니 다.여기 server. aoffd 즉 AOF 파일 설명자 입 니 다. 이 매개 변 수 는 initServer () 함수 에서 초기 화 됩 니 다./* Open the AOF file if needed. */
if (server.aof_state == REDIS_AOF_ON) {
server.aof_fd = open(server.aof_filename,O_WRONLY|O_APPEND|O_CREAT,0644);
if (server.aof_fd == -1) {
redisLog(REDIS_WARNING, "Can't open the append-only file: %s",strerror(errno));
exit(1);
}
}
이로써 레 디 스 서버 가 하드디스크 에 있 는 AOF 파일 데 이 터 를 불 러 오 는 작업 을 시작 하면 성공 적 으로 종료 된다.
서버 데이터베이스 에서 새로운 데 이 터 를 생 성하 는데 어떻게 하드디스크 에 오래 지속 합 니까
클 라 이언 트 가 Set 등 데이터베이스 에 있 는 필드 를 수정 하 는 명령 을 실행 할 때 Server 데이터베이스 에 있 는 데이터 가 수 정 됩 니 다. 이 수 정 된 데 이 터 는 AOF 파일 에 실시 간 으로 업데이트 되 어야 하고 일정한 fsync 체제 에 따라 하 드 디스크 에 새로 고침 되 어 데이터 가 손실 되 지 않도록 해 야 합 니 다.
지난 블 로그 에 서 는 세 가지 fsync 방식 을 언급 했다. appendfsync always. appendfsync everysec, appendfsync no. 구체 적 으로 server. aof 에 나타난다.fsync 매개 변수 중.
먼저 클 라 이언 트 가 요청 한 명령 으로 데이터 가 수정 되 었 을 때 Redis 는 데 이 터 를 수정 하 는 명령 을 server. aof 에 어떻게 추가 하 는 지 봅 니 다.buf 중의.
call() -> propagate() -> feedAppendOnly File (), call () 함수 가 명령 을 실행 한 후 데이터 가 수정 되 었 는 지 판단 합 니 다.
feedAppendOnly File 함 수 는 서버 가 AOF 를 열 었 는 지 여 부 를 먼저 판단 합 니 다. AOF 를 열 면 Redis 통신 프로 토 콜 에 따라 데 이 터 를 수정 하 는 명령 을 요청 한 문자열 로 재현 합 니 다. 시간 초과 설정 처리 방식 에 주의 하고 문자열 append 를 server. aofbuf 중 입 니 다.이 함수 의 마지막 두 줄 코드 는 주의해 야 합 니 다. 이것 이 중점 입 니 다. 만약 server. aofchild_pid != -1. 그러면 서버 가 rewrite AOF 파일 을 다시 쓰 고 있 음 을 나타 내 며 수 정 된 데 이 터 를 server. aof 에 추가 해 야 합 니 다.rewrite_buf_blocks 링크 에서 rewrite 가 끝나 면 AOF 파일 에 추가 합 니 다.구체 적 으로 아래 코드 의 주석 을 보십시오.
/* Propagate the specified command (in the context of the specified database id)
* to AOF and Slaves.
*
* flags are an xor between:
* + REDIS_PROPAGATE_NONE (no propagation of command at all)
* + REDIS_PROPAGATE_AOF (propagate into the AOF file if is enabled)
* + REDIS_PROPAGATE_REPL (propagate into the replication link)
*/
void propagate(struct redisCommand *cmd, int dbid, robj **argv, int argc,
int flags)
{
// cmd AOF
if (server.aof_state != REDIS_AOF_OFF && flags & REDIS_PROPAGATE_AOF)
feedAppendOnlyFile(cmd,dbid,argv,argc);
if (flags & REDIS_PROPAGATE_REPL)
replicationFeedSlaves(server.slaves,dbid,argv,argc);
}
//cmd , server.aof_buf
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 appendend. To issue a SELECT command is needed. */
// db aof db, SELECT
if (dictid != server.aof_selected_db) {
char seldb[64];
snprintf(seldb,sizeof(seldb),"%d",dictid);
buf = sdscatprintf(buf,"*2\r
$6\r
SELECT\r
$%lu\r
%s\r
",
(unsigned long)strlen(seldb),seldb);
server.aof_selected_db = dictid;
}
// EXPIRE / PEXPIRE / EXPIREAT PEXPIREAT
if (cmd->proc == expireCommand || cmd->proc == pexpireCommand ||
cmd->proc == expireatCommand) {
/* Translate EXPIRE/PEXPIRE/EXPIREAT into PEXPIREAT */
buf = catAppendOnlyExpireAtCommand(buf,cmd,argv[1],argv[2]);
}// SETEX / PSETEX SET PEXPIREAT
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 {//
/* 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. */
// buf aof_buf , beforeSleep AOF , fsync
if (server.aof_state == REDIS_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. */
// server.aof_child_pid 1, ( rewrite),
// , ,
// AOF ,
// aof_rewrite_buf_blocks, server rewrite ,
// AOF , ,
if (server.aof_child_pid != -1)
aofRewriteBufferAppend((unsigned char*)buf,sdslen(buf));
/* server.aof_buf server.aof_rewrite_buf_blocks
aof_buf aof , AOF 。
aof_rewrite_buf_blocks AOF , config set appendonly yes
redis fork , , 。
, , AOF ,serverCron
, backgroundRewriteDoneHandler aofRewriteBufferWrite ,
aof_rewrite_buf_blocks , diff AOF , unlink AOF 。
,aof_buf aof_rewrite_buf_blocks ,
aof_buf 。*/
sdsfree(buf);
}
서버 는 이벤트 가 순환 하기 전에 beforeSleep 함 수 를 한 번 호출 합 니 다. 이 함수 가 무슨 일 을 했 는 지 살 펴 보 겠 습 니 다.
/* This function gets called every time Redis is entering the
* main loop of the event driven library, that is, before to sleep
* for ready file descriptors. */
void beforeSleep(struct aeEventLoop *eventLoop) {
REDIS_NOTUSED(eventLoop);
listNode *ln;
redisClient *c;
/* Run a fast expire cycle (the called function will return
* ASAP if a fast cycle is not needed). */
if (server.active_expire_enabled && server.masterhost == NULL)
activeExpireCycle(ACTIVE_EXPIRE_CYCLE_FAST);
/* Try to process pending commands for clients that were just unblocked. */
while (listLength(server.unblocked_clients)) {
ln = listFirst(server.unblocked_clients);
redisAssert(ln != NULL);
c = ln->value;
listDelNode(server.unblocked_clients,ln);
c->flags &= ~REDIS_UNBLOCKED;
/* Process remaining data in the input buffer. */
//
if (c->querybuf && sdslen(c->querybuf) > 0) {
server.current_client = c;
processInputBuffer(c);
server.current_client = NULL;
}
}
/* Write the AOF buffer on disk */
// server.aof_buf AOF fsync
flushAppendOnlyFile(0);
}
위의 코드 와 주석 을 통 해 알 수 있 듯 이 beforeSleep 함수 가 세 가지 일 을 했 습 니 다. 1. 만 료 키 처리, 2. 차단 기간 의 클 라 이언 트 요청 처리, 3. server. aofbuf 의 데 이 터 를 AOF 파일 에 추가 하고 fsync 를 하 드 디스크 에 리 셋 합 니 다. flushAppendOnly File 함 수 는 매개 변수 force 를 지정 하여 AOF 파일 에 강제 적 으로 쓸 지 여 부 를 표시 합 니 다. 0 은 강제 적 이지 않 으 면 지연 쓰기 가 지원 되 고 1 은 강제 적 으로 쓸 지 여 부 를 표시 합 니 다.void flushAppendOnlyFile(int force) {
ssize_t nwritten;
int sync_in_progress = 0;
if (sdslen(server.aof_buf) == 0) return;
// fsync
if (server.aof_fsync == AOF_FSYNC_EVERYSEC)
sync_in_progress = bioPendingJobsOfType(REDIS_BIO_AOF_FSYNC) != 0;
// AOF fsync , force 1 ,
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. */
// aof_fsync
if (sync_in_progress) {
// , ,
if (server.aof_flush_postponed_start == 0) {
/* No previous write postponinig, 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++;
redisLog(REDIS_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.");
}
}
/* If you are following this code path, then we are going to write so
* set reset the postponed flush sentinel to zero. */
server.aof_flush_postponed_start = 0;
/* 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 */
// AOF , ,
nwritten = write(server.aof_fd,server.aof_buf,sdslen(server.aof_buf));
if (nwritten != (signed)sdslen(server.aof_buf)) {//
/* Ooops, we are in troubles. The best thing to do for now is
* aborting instead of giving the illusion that everything is
* working as expected. */
if (nwritten == -1) {
redisLog(REDIS_WARNING,"Exiting on error writing to the append-only file: %s",strerror(errno));
} else {
redisLog(REDIS_WARNING,"Exiting on short write while writing to "
"the append-only file: %s (nwritten=%ld, "
"expected=%ld)",
strerror(errno),
(long)nwritten,
(long)sdslen(server.aof_buf));
if (ftruncate(server.aof_fd, server.aof_current_size) == -1) {
redisLog(REDIS_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));
}
}
exit(1);
}
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). */
// aof , , , aof
if ((sdslen(server.aof_buf)+sdsavail(server.aof_buf)) < 4000) {
sdsclear(server.aof_buf);
} else {
sdsfree(server.aof_buf);
server.aof_buf = sdsempty();
}
/* Don't fsync if no-appendfsync-on-rewrite is set to yes and there are
* children doing I/O in the background. */
//aof rdb fsync aof rdb , ,
// aof ,
if (server.aof_no_fsync_on_rewrite &&
(server.aof_child_pid != -1 || server.rdb_child_pid != -1))
return;
/* Perform the fsync if needed. */
if (server.aof_fsync == AOF_FSYNC_ALWAYS) {// fsync, fsync
/* aof_fsync is defined as fdatasync() for Linux in order to avoid
* flushing metadata. */
aof_fsync(server.aof_fd); /* Let's try to get this data on the disk */
server.aof_last_fsync = server.unixtime;
} else if ((server.aof_fsync == AOF_FSYNC_EVERYSEC &&
server.unixtime > server.aof_last_fsync)) {
if (!sync_in_progress) aof_background_fsync(server.aof_fd);// fsync
server.aof_last_fsync = server.unixtime;
}
}
상기 코드 에서 server. aoffsync 매개 변수, 즉 Redis fsync AOF 파일 을 하 드 디스크 에 설정 하 는 정책 입 니 다. AOF 로 설정 하면FSYNC_ALWAYS, 메 인 프로 세 스에 서 fsync 를 직접 설정 합 니 다. AOF 로 설정 하면FSYNC_EVERYSEC, 그러면 배경 스 레 드 에 fsync 를 넣 고 배경 스 레 드 의 코드 는 bio. c 에 있 습 니 다.작은 매듭
이 글 은 Redis Server 가 AOF 파일 을 불 러 오 는 것 을 시작 하고 클 라 이언 트 가 요청 한 새로운 데 이 터 를 AOF 파일 에 추가 하 는 방법 이 해결 되 었 습 니 다. AOF 파일 에 추가 데 이 터 를 추가 하 는 방법 에 대해 fsync 설정 정책 에 따라 AOF 파일 에 기 록 된 새 데 이 터 를 하 드 디스크 에 어떻게 새로 고침 하 는 지, 메 인 프로 세 스에 서 fsync 또는 백 엔 드 스 레 드 fsync 에 직접 업데이트 합 니 다.
이로써 AOF 데이터 의 지속 화 는 클 라 이언 트 가 보 낸 BGREWRITEAOF 요청 을 받 아들 이 는 방법 이 남 았 으 며, 이 부분 은 다음 블 로그 에서 해석 해 야 한다.
이 블 로그 가 저 에 게 Redis AOF 데이터 의 지속 화 를 이해 하 는 데 큰 도움 을 주 셔 서 감사합니다.http://chenzhenianqing.cn/articles/786.html
본인 Redis - 2.8.2 의 소스 코드 주석 은 Github 에 넣 었 습 니 다. 필요 한 독자 가 다운로드 할 수 있 고 저도 후속 시간 에 업데이트 하 겠 습 니 다.https://github.com/xkeyideal/annotated-redis-2.8.2
제 가 Git 을 잘 못 쓰 는데 누가 좀 가르쳐 주세요.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Redis 해시에 대한 완벽한 가이드변경 가능하므로 필요에 따라 쉽게 변경하고 업데이트할 수 있습니다. Redis 해시는 구조가 평평하므로 JSON에서와 같이 여러 수준을 가질 수 없습니다. redis 해시의 명명 규칙은 hash:key 로 입력되므로...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.