Redis源码分析
来源:互联网 发布:为什么用 php curl 编辑:程序博客网 时间:2024/05/22 10:40
这一篇或者说一个系列用来记录Redis相关的一些源码分析,不定时更新。
目前已添加的内容:
- Redis之eventloop
- Redis数据结构之dict
Redis之eventloop
简介
Redis的eventloop实现也是比较平常的,主要关注文件描述符和timer相关事件,而且timer只是简单用一个单链表(O(n)遍历寻找最近触发的时间)实现。
流程
- 主要在initServer(server.c)中初始化整个eventloop相关的数据结构与回调
// 注册系统timer事件if (aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL) == AE_ERR) { serverPanic("Can't create event loop timers."); exit(1);}// 注册poll fd的接收客户端连接的读事件for (j = 0; j < server.ipfd_count; j++) { if (aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE, acceptTcpHandler,NULL) == AE_ERR) { serverPanic( "Unrecoverable error creating server.ipfd file event."); }}// 同上if (server.sofd > 0 && aeCreateFileEvent(server.el,server.sofd,AE_READABLE, acceptUnixHandler,NULL) == AE_ERR) serverPanic("Unrecoverable error creating server.sofd file event.");
- acceptTcpHandler处理客户端请求,分配client结构,注册事件
cfd = anetTcpAccept(server.neterr, fd, cip, sizeof(cip), &cport);acceptCommonHandler(cfd,0,cip);
- createClient,创建客户端
// receieved a client, alloc client structure // and register it into eventpollclient *createClient(int fd) {client *c = zmalloc(sizeof(client));if (fd != -1) { anetNonBlock(NULL,fd); anetEnableTcpNoDelay(NULL,fd); if (server.tcpkeepalive) anetKeepAlive(NULL,fd,server.tcpkeepalive); // register read event for client connection // the callback handler is readQueryFromClient // read into client data buffer if (aeCreateFileEvent(server.el,fd,AE_READABLE, readQueryFromClient, c) == AE_ERR) { close(fd); zfree(c); return NULL; }}
- client读事件触发,读到buffer,解析client命令
dQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) --> processInputBuffer // handle query buffer// in processInputBuffer(c);if (c->reqtype == PROTO_REQ_INLINE) { if (processInlineBuffer(c) != C_OK) break;} else if (c->reqtype == PROTO_REQ_MULTIBULK) { if (processMultibulkBuffer(c) != C_OK) break;} else { serverPanic("Unknown request type");}/* Multibulk processing could see a <= 0 length. */if (c->argc == 0) { resetClient(c);} else { /* Only reset the client when the command was executed. */ // handle the client command if (processCommand(c) == C_OK) resetClient(c); /* freeMemoryIfNeeded may flush slave output buffers. This may result * into a slave, that may be the active client, to be freed. */ if (server.current_client == NULL) break;}
- 处理客户端命令
// in processCommand /* Exec the command */if (c->flags & CLIENT_MULTI && c->cmd->proc != execCommand && c->cmd->proc != discardCommand && c->cmd->proc != multiCommand && c->cmd->proc != watchCommand){ queueMultiCommand(c); addReply(c,shared.queued);} else { // call the cmd // 进入具体数据结构的命令处理 call(c,CMD_CALL_FULL); c->woff = server.master_repl_offset; if (listLength(server.ready_keys)) handleClientsBlockedOnLists();}
其他注意点
- 关于timer的实现没有采用优先级队列(O(logn))等其他数据结构,而是直接采用O(n)遍历的单链表,是因为一般来说timer会较少?
Redis数据结构之dict
主要特点
Redis的hashtable实现叫dict,其实现和平常没有太大的区别,唯一比较特殊的地方是每个dict结构内部有两个实际的hashtable结构dictht,是为了实现增量哈希,故名思义,即当第一个dictht到一定负载因子后会触发rehash,分配新的dictht结构的动作和真正的rehash的动作是分离的,并且rehash被均摊到各个具体的操作中去了,这样就不会长时间阻塞线程,因为Redis是单线程。另外,增量hash可以按多步或者持续一定时间做。
主要数据结构
- dictEntry => hashtable的bucket
- dictType => 规定操作hashtable的接口
- dictht => hashtable
- dict => 对外呈现的”hashtable”
- dictIterator => 迭代器,方便遍历
// dict.h// hash table entry typedef struct dictEntry { void *key; // key union { void *val; uint64_t u64; int64_t s64; double d; } v; // value struct dictEntry *next; // linked list } dictEntry;// operations(APIS) of some type of hashtabletypedef struct dictType { // hash function unsigned int (*hashFunction)(const void *key); // copy key void *(*keyDup)(void *privdata, const void *key); // copy value void *(*valDup)(void *privdata, const void *obj); // key comparison int (*keyCompare)(void *privdata, const void *key1, const void *key2); // dtor for key void (*keyDestructor)(void *privdata, void *key); // dtor for value void (*valDestructor)(void *privdata, void *obj);} dictType;/* This is our hash table structure. Every dictionary has two of this as we * implement incremental rehashing, for the old to the new table. */// a hashtable typedef struct dictht { dictEntry **table; // entries unsigned long size; // max size unsigned long sizemask; // mask unsigned long used; // current used } dictht;typedef struct dict { dictType *type; // type operations void *privdata; // for extension dictht ht[2]; // two hashtables // rehashing flag long rehashidx; /* rehashing not in progress if rehashidx == -1 */ // users number unsigned long iterators; /* number of iterators currently running */} dict;/* If safe is set to 1 this is a safe iterator, that means, you can call * dictAdd, dictFind, and other functions against the dictionary even while * iterating. Otherwise it is a non safe iterator, and only dictNext() * should be called while iterating. */typedef struct dictIterator { dict *d; long index; int table, safe; dictEntry *entry, *nextEntry; /* unsafe iterator fingerprint for misuse detection. */ long long fingerprint;} dictIterator;
主要接口
// dict.h// createdict *dictCreate(dictType *type, void *privDataPtr);// expand or initilize the just created dict, alloc second hashtable of dict for incremental rehashingint dictExpand(dict *d, unsigned long size);// add, if in rehashing, do 1 step of incremental rehashingint dictAdd(dict *d, void *key, void *val);dictEntry *dictAddRaw(dict *d, void *key);// update, if in rehashing, do 1 step of incremental rehashing// can we first find and return the entry no matter it is update or add, so // we can speed up the update process because no need to do twice find process?int dictReplace(dict *d, void *key, void *val);dictEntry *dictReplaceRaw(dict *d, void *key);// delete if in rehashing, do 1 step of incremental rehashingint dictDelete(dict *d, const void *key); // free the memory int dictDeleteNoFree(dict *d, const void *key); // not free the memory// can we use a double linked list to free the hash table, so speed up?void dictRelease(dict *d);// find an entrydictEntry * dictFind(dict *d, const void *key);void *dictFetchValue(dict *d, const void *key);// resize to eh pow of 2 number just >= the used number of slotsint dictResize(dict *d);// alloc a new iteratordictIterator *dictGetIterator(dict *d);// alloc a safe iterator dictIterator *dictGetSafeIterator(dict *d);// next entry dictEntry *dictNext(dictIterator *iter);void dictReleaseIterator(dictIterator *iter);// random samplingdictEntry *dictGetRandomKey(dict *d);unsigned int dictGetSomeKeys(dict *d, dictEntry **des, unsigned int count);// get stats infovoid dictGetStats(char *buf, size_t bufsize, dict *d);// murmurhash unsigned int dictGenHashFunction(const void *key, int len);unsigned int dictGenCaseHashFunction(const unsigned char *buf, int len);// empty a dict void dictEmpty(dict *d, void(callback)(void*));void dictEnableResize(void);void dictDisableResize(void);// do n steps rehashingint dictRehash(dict *d, int n);// do rehashing for a ms millisecondsint dictRehashMilliseconds(dict *d, int ms);// hash function seed void dictSetHashFunctionSeed(unsigned int initval);unsigned int dictGetHashFunctionSeed(void);// scan a dictunsigned long dictScan(dict *d, unsigned long v, dictScanFunction *fn, void *privdata);
一些可能优化的地方
在dictReplace中能否统一add和update的查找,无论是add还是update都返回一个entry,用标识表明是add还是update,而不用在update时做两次查找,从而提升update的性能
在release整个dict时,是循环遍历所有头bucket,最坏情况接近O(n),能否用双向的空闲链表优化(当然这样会浪费指针所占空间)
0 0
- Redis源码分析:AOF
- Redis源码分析:snapshot
- redis源码分析----序言
- Redis源码分析-TCMalloc
- Redis Sentinel 源码分析
- Redis源码简要分析
- Redis源码简要分析
- scrapy-redis源码分析
- Redis源码简要分析
- Redis源码简要分析
- redis源码分析实践
- redis源码分析
- Redis源码分析系列
- redis源码分析
- Redis源码分析
- Redis源码分析:AOF
- Redis源码分析:snapshot
- Redis源码简要分析
- 《禅者的初心》读书笔记(3)
- 1030. 完美数列(25)
- Codeforces 150E Freezing with Style (树分治)
- 模糊查询时能查到英文字段,但是查不到中文字段原因
- Android 图片压缩之多种压缩方式结合使用
- Redis源码分析
- 生活管家app
- Tinyhttp源码分析
- c语言学习笔记17之函数
- 【面试】【MySQL常见问题总结】【03】
- Struts2入门详解
- 二分查找
- C primer plus 第九章 练习3:
- Android RelativeLayout 属性详解