Redis源代码分析
Redis源代码分析
Redis
(huwu)
(yuhanzou)
1 / 32
Redis · Arch_Platform
2 / 32
Redis · Arch_Platform
1. Redis ! 4
2. ! 4
2.1. (adlist.h/adlist.c)! 4
2.2. (sds.h/sds.c)! 5
2.3. (dict.h/dict.c)! 6
2.4. (zmalloc.h/zmalloc.h)! 9
3. ! 11
3.1. (ae.h/ae.c)! 11
3.2. (anet.h/anet.c)! 15
3.4. ! 17
4. ! 19
4.1. ! 20
4.2. ! 23
5. ! 24
5.1. Snapshot! 24
5.2. AOF! 26
6. ! 27
6.1. ! 27
6.2. ! 30
6.3. ! 31
3 / 32
Redis · Arch_Platform
1. Redis
Redis(http://redis.io) Key-Value string
hash list set sorted set
2.
2.1. (adlist.h/adlist.c)
(list) Redis adlist.h adlist.c
listNode (next)
(prev) void*
typedef struct listNode {
struct listNode *prev;
struct listNode *next;
void *value;
} listNode;
list (head) (tail)
(dup) (free)
(match) (value) len
typedef struct list {
listNode *head;
listNode *tail;
void *(*dup)(void *ptr);
void (*free)(void *ptr);
int (*match)(void *ptr, void *key);
unsigned int len;
} list;
listIter (next) direction
( AL_START_HEAD AL_START_TAIL
typedef struct listIter {
listNode *next;
int direction;
} listIter;
Redis list
(listCreate) Redis zmalloc()
(listRelease) (listDelNode) (list)
free Redis (value)
Redis zfree()
listGetIterator() listNext()
listReleaseIterator() listRewind() listRewindTail()
list
iter = listGetIterator(list, AL_START_HEAD); //
4 / 32
Redis · Arch_Platform
2.2. (sds.h/sds.c)
Redis Redis key value
key Redis (Binary Safe)
256 (8bit)
[2] Redis value key
6 0 qqmail
5 / 32
Redis · Arch_Platform
sds
sds sdsnewlen(const void *init, size_t initlen);
sds sdsnew(const char *init);
sds sdsempty();
zmalloc() sdsempty() void
sdsfree(sds s) size_t sdslen(const sds s)
size_t sdsavail(sds s)
sds
sds sdsgrowzero(sds s, size_t len);
sds sdscatlen(sds s, void *t, size_t len);
sds sdscat(sds s, char *t);
sds sdscpylen(sds s, char *t, size_t len);
sds sdscpy(sds s, char *t);
sdsgrowzero() sds ’\0’ free
sdscat() t s sdscpy() t s s
sdsgrowzero()
sds
2.3. (dict.h/dict.c)
Redis 0/1
dict type
dictht ht[2] rehashidx
iterators
typedef struct dict {
dictType *type;
void *privdata;
dictht ht[2];
int rehashidx; /* rehashing not in progress if rehashidx == -1 */
int iterators; /* number of iterators currently running */
} dict;
dictht table
size 2 sizemark size-1
size used
typedef struct dictht {
dictEntry **table;
unsigned long size;
unsigned long sizemask;
unsigned long used;
} dictht;
dictType
Hash Key Value
Key Key Value
type
typedef struct dictType {
unsigned int (*hashFunction)(const void *key);
void *(*keyDup)(void *privdata, const void *key);
6 / 32
Redis · Arch_Platform
Redis dict_can_resize
dictEnableResize() dictDisableResize()
dictResize()
dictExpand()
dictRehashMilliseconds()
dictAdd()
_dictExpandIfNeeded() _dictExpandIfNeeded
size 0 0
used>=size can_resize==1 used/size 5
max(used, size) dictExpand()
if (dictIsRehashing(d)) return DICT_OK;
if (d->ht[0].size == 0) return dictExpand(d, DICT_HT_INITIAL_SIZE);
n.size = realsize;
n.sizemask = realsize-1;
n.table = zcalloc(realsize*sizeof(dictEntry*));
n.used = 0;
if (d->ht[0].table == NULL) {
d->ht[0] = n;
7 / 32
Redis · Arch_Platform
return DICT_OK;
}
d->ht[1] = n;
d->rehashidx = 0;
return DICT_OK;
dictIsRehashing() rehashidx
dictAdd()
_dictRehashStep() iterators==0
ht[0] ht[1]
dictRehash() 0 ht[0]
ht[1] rehashidx -1 1
ht[0]->table[rehashidx] ht[1] rehashidx++
used==0
ht[1] dictRehash()
while(n--) {
dictEntry *de, *nextde;
if (d->ht[0].used == 0) {
zfree(d->ht[0].table);
d->ht[0] = d->ht[1];
_dictReset(&d->ht[1]);
d->rehashidx = -1;
return 0;
}
while(de) {
unsigned int h;
nextde = de->next;
/* Get the index in the new hash table */
h = dictHashKey(d, de->key) & d->ht[1].sizemask;
de->next = d->ht[1].table[h];
d->ht[1].table[h] = de;
d->ht[0].used--;
d->ht[1].used++;
de = nextde;
}
d->ht[0].table[d->rehashidx] = NULL;
d->rehashidx++;
}
return 1;
}
8 / 32
Redis · Arch_Platform
dictGetIterator()
dictNext(dictIterator *)
0
ht[0] table[0]
table[1], table[2], ..., table[size]
ht[1]
2.4. (zmalloc.h/zmalloc.h)
Redis , zmalloc(),
zrealloc() zcalloc() zfree(), C malloc(), realloc()
calloc() free() zmalloc.h zmalloc.c
Redis
Redis (VM)
swap
malloc()
NULL
calloc() malloc()
NULL calloc() count size
calloc() 0
realloc()
NULL
free()
#if defined(USE_TCMALLOC)
#define malloc(size) tc_malloc(size)
zmalloc()zfree() Redis
size (PREFIX_SIZE)
Redis Redis
update_zmalloc_stat_alloc(size+PREFIX_SIZE, size)
zfree()
update_zmalloc_stat_free()
malloc_size()
Mac OS X 10.4 [3] malloc_size()
PREFIX_SIZE
9 / 32
Redis · Arch_Platform
if (!ptr) zmalloc_oom(size);
#ifdef HAVE_MALLOC_SIZE
update_zmalloc_stat_alloc(redis_malloc_size(ptr),size);
return ptr;
#else
*((size_t*)ptr) = size; //
update_zmalloc_stat_alloc(size+PREFIX_SIZE,size);
return (char*)ptr+PREFIX_SIZE;
#endif
}
update_zmalloc_stat_alloc()
zmalloc_thread_safe
Redis thread_safe (VM)
dump
Redis zstrdup(char
*) memcpy()
10 / 32
Redis · Arch_Platform
3.
3.1. (ae.h/ae.c)
Redis ( )
Redis ae_epoll.h/
ae_epoll.c epoll ae_select.h/ae_select.c select
ae_kqueue.h/ae_kqueue.c bsd kqueue
...
retval = te->timeProc(eventLoop, id, te->clientData);
processed++;
if (retval != AE_NOMORE) {
aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms);
} else {
aeDeleteTimeEvent(eventLoop, id);
}
...
Redis API aeWait
(int fd, int mask, long long mileseconds) fd
mask mileseconds select
Redis aeSetBeforeSleepProc()
13 / 32
Redis · Arch_Platform
if ((pid = wait3(&statloc,WNOHANG,NULL)) != 0) {
if (pid == server.bgsavechildpid) {
backgroundSaveDoneHandler(statloc);
} else {
backgroundRewriteDoneHandler(statloc);
}
updateDictResizePolicy();
}
} else {
/* If there is not a background saving in progress check if
* we have to save now */
time_t now = time(NULL);
for (j = 0; j < server.saveparamslen; j++) {
struct saveparam *sp = server.saveparams+j;
/* Swap a few keys on disk if we are over the memory limit and VM
* is enbled. Try to free objects from the free list first. */
if (vmCanSwapOut()) {
while (server.vm_enabled && zmalloc_used_memory() >
server.vm_max_memory)
{
int retval = (server.vm_max_threads == 0) ?
vmSwapOneObjectBlocking() :
vmSwapOneObjectThreaded();
if (retval == REDIS_ERR && !(loops % 300) &&
zmalloc_used_memory() >
(server.vm_max_memory+server.vm_max_memory/10))
{
redisLog(REDIS_WARNING,"WARNING: vm-max-memory limit exceeded by
more than 10%% but unable to swap more objects out!");
}
14 / 32
Redis · Arch_Platform
/* Note that when using threade I/O we free just one object,
* because anyway when the I/O thread in charge to swap this
* object out will finish, the handler of completed jobs
* will try to swap more objects if we are still out of memory. */
if (retval == REDIS_ERR || server.vm_max_threads > 0) break;
}
}
slave 10 serverCron 1 master
3.2. (anet.h/anet.c)
Redis anet.h/c :
int anetTcpConnect(char *err, char *addr, int port);
int anetTcpNonBlockConnect(char *err, char *addr, int port);
int anetUnixConnect(char *err, char *path);
int anetUnixNonBlockConnect(char *err, char *path);
int anetRead(int fd, char *buf, int count);
int anetResolve(char *err, char *host, char *ipbuf);
int anetTcpServer(char *err, int port, char *bindaddr);
int anetUnixServer(char *err, char *path);
int anetTcpAccept(char *err, int serversock, char *ip, int *port);
int anetUnixAccept(char *err, int serversock);
int anetWrite(int fd, char *buf, int count);
int anetNonBlock(char *err, int fd);
int anetTcpNoDelay(char *err, int fd);
int anetTcpKeepAlive(char *err, int fd);
anetTcpServer() socket() bind()
listen() socket fd anetUnixServer()
anetTcpConnect() anetTcpNonBlockConnect()
anetUnixConnect() anetUnixNonBlockConnect()
15 / 32
Redis · Arch_Platform
main() initServerConfig()
command table(server.commands)
Redis dict Hash command
table commandTableDictType command dict
populateCommand() Redis redisCommand
Redis command table
main() initServer() anetTcpServer
()
initServer() {
// ....
server.ipfd = anetTcpServer(server.neterr,server.port,server.bindaddr);
// ...
if (server.ipfd > 0 && aeCreateFileEvent(server.el,server.ipfd,AE_READABLE,
acceptTcpHandler,NULL) == AE_ERR) oom("creating file event");
// ...
}
Redis aeCreateFileEvent()
READABLE acceptTcpHandler()
READABLE anetTcpAccept() fd fd
createClient() redisClient
fd buf buf Redis
epoll
Redis
Redis
Redis
Redis redisClient(redis.h)
createClient() freeClient()
createClient() fd READABLE
readQueryFromClient()
server.clients
/* With multiplexing we need to take per-clinet state.
* Clients are taken in a liked list. */
typedef struct redisClient {
int fd;
redisDb *db;
int dictid;
sds querybuf;
int argc;
robj **argv;
int reqtype;
int multibulklen; /* number of multi bulk arguments left to read */
long bulklen; /* length of bulk argument in multi bulk request */
list *reply;
int sentlen;
time_t lastinteraction; /* time of the last interaction, used for timeout
*/
int flags; /* REDIS_SLAVE | REDIS_MONITOR | REDIS_MULTI ...
0 */
16 / 32
Redis · Arch_Platform
/* Response buffer */
int bufpos;
char buf[REDIS_REPLY_CHUNK_BYTES];
} redisClient;
freeClient()
(server.maxclients)
Redis
REDIS_BLOCKED REDIS_IO_WAIT
REDIS_CLOSE_AFTER_REPLY
3.4.
Redis redisCommand
typedef void redisCommandProc(redisClient *c);
typedef void redisVmPreloadProc(redisClient *c, struct redisCommand *cmd, int
argc, robj **argv);
struct redisCommand {
char *name;
redisCommandProc *proc;
int arity;
int flags;
redisVmPreloadProc *vm_preload_proc;
int vm_firstkey;
int vm_lastkey;
int vm_keystep;
};
Redis readQueryFromClient() Input
REDIS_IOBUF_LEN 1024 redisClient
querybuf sdscatlen(c->querybuf,buf,nread)
processInputBuffer()
ready flags REDIS_BLOCKED REDIS_IO_WAIT
REDIS_CLOSE_AFTER_REPLY reqtype
querybuf[0]==’*’
MULTIBULK INLINE
(resetClient())
17 / 32
Redis · Arch_Platform
if (!c->reqtype) {
if (c->querybuf[0] == '*') {
c->reqtype = REDIS_REQ_MULTIBULK;
} else {
c->reqtype = REDIS_REQ_INLINE;
}
}
if (c->reqtype == REDIS_REQ_INLINE) {
if (processInlineBuffer(c) != REDIS_OK) break;
} else if (c->reqtype == REDIS_REQ_MULTIBULK) {
if (processMultibulkBuffer(c) != REDIS_OK) break;
} else {
redisPanic("Unknown request type");
}
if (c->argc == 0) {
resetClient(c);
} else {
if (processCommand(c) == REDIS_OK)
resetClient(c);
}
}
}
INLINE (‘\r\n’)
processInlineBuffer() argc/argv argv
redisObject
MULTIBULK //TODO
processCommand() ”quit” Redis
lookupCommand() command table
(cmd->proc())
‘get’ readonlyCommandTable initServerConfig
() command table get
lookupCommand() redisCommand
getCommand()
struct redisCommand readonlyCommandTable[] = {
{"get",getCommand,2,0,NULL,1,1,1},
// ....
}
redis t_***.c t_string.c
getCommand()
18 / 32
Redis · Arch_Platform
4.
Redis Redis Redis
Redis 2.0
value
vm vm
Redis
Redis redisObject
/* The actual Redis Object */
#define REDIS_LRU_CLOCK_MAX ((1<<21)-1) /* Max value of obj->lru */
#define REDIS_LRU_CLOCK_RESOLUTION 10 /* LRU clock resolution in seconds */
typedef struct redisObject {
unsigned type:4;
unsigned storage:2; /* REDIS_VM_MEMORY or REDIS_VM_SWAPPING */
unsigned encoding:4;
unsigned lru:22; /* lru time (relative to server.lruclock) */
int refcount;
void *ptr;
/* VM fields are only allocated if VM is active, otherwise the
* object allocation function will just allocate
* sizeof(redisObjct) minus sizeof(redisObjectVM), so using
* Redis without VM active will not have any overhead. */
} robj;
type value string list set storage
Redis
encoding lru server.lruclock refcount
19 / 32
Redis · Arch_Platform
vmpointer 32 redisObject
vmPointer
off_t vm_page_size;
off_t vm_pages;
off_t vm_next_page; /* Next probably empty page */
off_t vm_near_pages; /* Number of pages allocated sequentially */
unsigned char *vm_bitmap; /* Bitmap of free/used pages */
Redis (Page) Redis
server.vm_bitmap 0 1 Redis
/* An I/O thread process an element taken from the io_jobs queue and
* put the result of the operation in the io_done list. While the
* job is being processed, it's put on io_processing queue. */
list *io_newjobs; /* List of VM I/O jobs yet to be processed */
list *io_processing; /* List of VM I/O jobs being processed */
list *io_processed; /* List of VM I/O jobs already processed */
Redis VM (job) io_newjobs
job processing processed job Job 3
LOAD PREPARE_SWAP DO_SWAP
4.1.
getCommand() Redis Redis lookupKey()
dictEntity
dictEntity
dictEntity (REDIS_VM_MEMORY)
20 / 32
Redis · Arch_Platform
(REDIS_VM_SWAPPING) vmLoadObject()
dictEntity vmLoadObject()
processCommand()
if (server.vm_enabled && server.vm_max_threads > 0 &&
blockClientOnSwappedKeys(c,cmd)) return REDIS_ERR;
call(c,cmd);
blockClienkOnSwappedKeys
()
REDIS_IO_WAIT blockClienkOnSwappedKeys()
(vm_preload_proc)
waitForMultipleSwappedKeys() key
int blockClientOnSwappedKeys(redisClient *c, struct redisCommand *cmd) {
if (cmd->vm_preload_proc != NULL) {
cmd->vm_preload_proc(c,cmd,c->argc,c->argv);
} else {
waitForMultipleSwappedKeys(c,cmd,c->argc,c->argv);
}
/* If the client was blocked for at least one key, mark it as blocked. */
if (listLength(c->io_keys)) {
c->flags |= REDIS_IO_WAIT;
aeDeleteFileEvent(server.el,c->fd,AE_READABLE);
server.vm_blocked_clients++;
return 1;
} else {
return 0;
}
}
waitForMultipleSwappedKeys
waitForSwappedKey() cmd
dictEntity REDIS_VM_MEMORY
REDIS_VM_SWAPPING vmCancelThreadedIOJob(o)
key job
job job vmpoiner dictEntity
int waitForSwappedKey(redisClient *c, robj *key) {
struct dictEntry *de;
robj *o;
list *l;
21 / 32
Redis · Arch_Platform
return 0;
} else if (o->storage == REDIS_VM_SWAPPING) {
/* We were swapping the key, undo it! */
vmCancelThreadedIOJob(o);
return 0;
}
/* Add the key to the list of keys this client is waiting for.
* This maps clients to keys they are waiting for. */
listAddNodeTail(c->io_keys,key);
incrRefCount(key);
/* Add the client to the swapped keys => clients waiting map. */
de = dictFind(c->db->io_keys,key);
if (de == NULL) {
int retval;
/* Are we already loading the key from disk? If not create a job */
if (o->storage == REDIS_VM_SWAPPED) {
iojob *j;
vmpointer *vp = (vmpointer*)o;
o->storage = REDIS_VM_LOADING;
j = zmalloc(sizeof(*j));
j->type = REDIS_IOJOB_LOAD;
j->db = c->db;
j->id = (robj*)vp;
j->key = key;
incrRefCount(key);
j->page = vp->page;
j->val = NULL;
j->canceled = 0;
j->thread = (pthread_t) -1;
lockThreadedIO();
queueIOJob(j);
unlockThreadedIO();
}
return 1;
}
vm job queueIOJob
job server->io_newjobs IO
IO Job
void queueIOJob(iojob *j) {
redisLog(REDIS_DEBUG,"Queued IO Job %p type %d about key '%s'\n",
(void*)j, j->type, (char*)j->key->ptr);
listAddNodeTail(server.io_newjobs,j);
22 / 32
Redis · Arch_Platform
server->io_ready_pipe_read server->io_ready_pipe_write
vmInit() io_ready_pipe_read
vmThreadedIOCompletedJob job
vmThreadedIOCompletedJob job “ ”
REDIS_IOJOB_LOAD key vm
handleClientsBlockedOnSwappedKey key c-
>db->io_keys server->db->io_keys) {key, c} key
db->io_keys c dontWaitForSwappedKey()
db->io_keys c->io_keys key
key c->io_keys c key
server.io_ready_clients “ ”
server.io_ready_clients
READABLE Redis aeMain()
epoll beforeSleep() Redis
server.io_ready_clients ready READABLE
4.2.
Redis (Blocking Virtual
Memory) (Threaded Virtual Memory IO) vm.c
23 / 32
Redis · Arch_Platform
vmSwapOneObjectBlocking() vmSwapOneObjectThreaded()
vmSwapOneObject() Redis
5 computeObjectSwappability()
1
REDIS_IOJOB_DO_SWAP job
Redis vm
REDIS_VM_MAX_RANDOM_JUMP/4
REDIS_VM_MAX_RANDOM_JUMP
5.
Redis ——snapshot aof dump
snapshot Redis dump aof
Redis dump
5.1. Snapshot
Redis Snapshot Redis N
M Snapshot save
bgsave
save Redis bgsave Redis
dump
saveCommand() bgsave
rdbSave()
bgsaveCommand() bgsave
rdbSaveBackground()
rdbSave()
rdbSaveBackground() waitEmptyIOJobsQueue()
vm IO vm IO
fork() server.bgsavechildpid pid
updateDictResizePolicy() Hash
vm vm swap rdbSave() vm
IO vm swap vm IO
int rdbSaveBackground(char *filename) {
pid_t childpid;
if ((childpid = fork()) == 0) {
/* Child */
if (server.vm_enabled) vmReopenSwapFile();
if (server.ipfd > 0) close(server.ipfd);
if (server.sofd > 0) close(server.sofd);
if (rdbSave(filename) == REDIS_OK) {
_exit(0);
} else {
_exit(1);
}
} else {
/* Parent */
if (childpid == -1) {
redisLog(REDIS_WARNING,"Can't save in background: fork: %s",
strerror(errno));
return REDIS_ERR;
}
redisLog(REDIS_NOTICE,"Background saving started by pid %d",childpid);
server.bgsavechildpid = childpid;
updateDictResizePolicy();
return REDIS_OK;
}
return REDIS_OK; /* unreached */
}
rdbSave() waitEmptyIOJobsQueue() vm IO
vm IO vm swap vm
IO
tmp db server.db,
server.db+1, ..., server.db+server.dbnum dict
dictIterator db tmp
Redis rdbSaveLen()
32bit Redis 6bit 6bit
14bit 14bit 32bit REDIS_RDB_6BITLEN(00)
REDIS_RDB_14BITLEN(01) REDIS_RDB_32BITLEN(10)
Protobuf
25 / 32
Redis · Arch_Platform
5.2. AOF
Snapshot Redis AOF( )
commit log commit log
Redis commit log Redis
Buffer commit log
fsync() fsync
() fsync() fsync()
• rewriteAppendOnlyFile() aof
rewriteAppendOnlyFile() rewriteAppendOnlyFileBackground()
Redis AOF
(1) fork() rewrite rewriteAppendOnlyFile()
(2) rewrite rewriteAppendOnlyFile()
AOF
(3)rewrite
server.bgrewriteaofbuf
(4) serverCron() wait3()
backgroundRewriteDoneHandler() server.bgrewriteaofbuf
commit log server.aofbuf
(5) rewrite aof
Redis AOF
commit log
rewrite aofbuf
• flushAppendOnlyFile() buffer commit log
Redis beforeSleep() AOF buffer log
• loadAppendOnlyFile() AOF Redis AOF
6.
Redis http://www.redis.io/topics/
replication Redis
• Master Slave
• Slave Slave Masters Slaves
• Master Slave
Slave
• Slaves
• Slave Master
6.1.
Redis Slave Master Redis
server.replstate Master Slave
27 / 32
Redis · Arch_Platform
Slave server.replstate!=REDIS_REPL_CONNECTED
Slave Master c.replstate!
=REDIS_REPL_ONLINE
serverCron() 10 replicationCron()
replicationCron() Slave Master
6.1.1. Master
Redis Master Slave Slave
Sync Master Master Master Slave
syncCommand()
sync Slave
sync Slave Master sync
Slave Master sync
Master bgsave bgsave
Slave sync dump Slave replstate
REDIS_REPL_BGSAVE_END Slave replstate
REDIS_REPL_BGSAVE_END dump Slave dump
28 / 32
Redis · Arch_Platform
/* Refuse SYNC requests if we are a slave but the link with our master
* is not ok... */
if (server.masterhost && server.replstate != REDIS_REPL_CONNECTED) {
addReplyError(c,"Can't SYNC while not connected with my master");
return;
}
/* SYNC can't be issued when the server has pending data to send to
* the client about already issued commands. We need a fresh reply
* buffer registering the differences between the BGSAVE and the current
* dataset, so that we can copy to other slaves if needed. */
if (listLength(c->reply) != 0) {
addReplyError(c,"SYNC is invalid with pending input");
return;
}
listRewind(server.slaves,&li);
while((ln = listNext(&li))) {
slave = ln->value;
if (slave->replstate == REDIS_REPL_WAIT_BGSAVE_END) break;
}
if (ln) {
/* Perfect, the server is already registering differences for
* another slave. Set the right state, and copy the buffer. */
listRelease(c->reply);
c->reply = listDup(slave->reply);
c->replstate = REDIS_REPL_WAIT_BGSAVE_END;
redisLog(REDIS_NOTICE,"Waiting for end of BGSAVE for SYNC");
} else {
/* No way, we need to wait for the next BGSAVE in order to
* register differences */
c->replstate = REDIS_REPL_WAIT_BGSAVE_START;
redisLog(REDIS_NOTICE,"Waiting for next BGSAVE for SYNC");
}
} else {
/* Ok we don't have a BGSAVE in progress, let's start one */
redisLog(REDIS_NOTICE,"Starting BGSAVE for SYNC");
29 / 32
Redis · Arch_Platform
if (rdbSaveBackground(server.dbfilename) != REDIS_OK) {
redisLog(REDIS_NOTICE,"Replication failed, can't BGSAVE");
addReplyError(c,"Unable to perform background save");
return;
}
c->replstate = REDIS_REPL_WAIT_BGSAVE_END;
}
c->repldbfd = -1;
c->flags |= REDIS_SLAVE;
c->slaveseldb = 0;
listAddNodeTail(server.slaves,c);
return;
}
Snapshot bgsave serverCron()
backgroundSaveDoneHandler()
updateSlavesWaitingBgsave() bgsave Slave
updateSlavesWaitingBgsave() server.slaves
REDIS_REPL_WAIT_BGSAVE_START bgsave dump
REDIS_REPL_WAIT_BGSAVE_START
Slave Slave REDIS_REPL_SEND_BULK Master Slave
WRITEABLE Slave sendBulkToSlave() Slave
Master sendBulkToSlave() socket
Slave WRITEABLE Slave REDIS_REPL_ONLINE
Slave WRITEABLE Slave Slave
6.1.2. Slave
Redis Slave Master Slave
REDIS_REPL_ONLINE replicationCron()
syncWithMaster() Master
syncWithMaster() Master sync
readSyncBulkPayload() Master dump Slave syncRead()
syncWrite() Master IO
Slave
replicationCron() Master dump
dump Slave replicationAbortSyncTransfer()
6.2.
call() Master replicationFeedSlaves()
Slaves Slave Master
serverCron() Master Slave Master
DEL
30 / 32
Redis · Arch_Platform
6.3.
slaveof Redis Slave slaveof no one
Slave Master Slave Master slaveof ip port
Slave
Redis
server.masterhost
31 / 32
Redis · Arch_Platform
32 / 32