http://blog.csdn.net/lazybin/article/category/1255844
Redis(Remote Dictionary Server)是一种内存Key/Value数据库。所有的Key/Value都是存放在内存中,如果内存不足,会将一些value swap到硬盘,但是Key始终都在内存中。Redis类似于Memcached。但是redis比memcached有更丰富的数据结构,还可以支持备份,数据持久化(snapshot和aof)。
具体的二者区别可以参考http://blog.csdn.net/gpcuster/article/details/5956555 。
下图是一张是从网上获取的关于redis内部的存储结构(不知道原作者是谁了)。
最近在阅读redis源代码,决定将自己的一些理解记下来,用于备份和检查。无奈技术水平很挫,如果有错误,还希望指正。代码版本是2.6.2,代码量比2.4.17大了很多。==!
- adlist.h 定义了一个双链表结构。
- typedef struct listNode {
- struct listNode *prev;
- struct listNode *next;
- void *value;
- } listNode;
- typedef struct listIter {
- listNode *next;
- int direction;
- } listIter;
- typedef struct list {
- listNode *head;
- listNode *tail;
- void *(*dup)(void *ptr); //用于节点value的copy
- void (*free)(void *ptr); //用于节点value的释放
- int (*match)(void *ptr, void *key); //节点value的比较
- unsigned long len; //链表的长度
- } list;
list *listInsertNode(list *list, listNode *old_node, void *value, int after) ;//根据after是否为0来决定是在old_node节点之前(after == 0)还是之后(after != 0)
listNode *listIndex(list *list, long index);//返回链表中下标为index的节点,0为head节点,1为head->next节点,以此类推。若index为负数,则从后向前,-1为tail节点,-2为
tail->prev 节点以此类推。
list 数据结构不是太难理解~ 下文将会分析sds数据结构(作者自定义的字符串)
redis 源代码之数据结构(2)--sds实现
1,sds(simple dynamic string)作为redis作者自己实现的字符串类型,是redis的基本数据类型。
- typedef char *sds;
- struct sdshdr {
- int len;
- int free;
- char buf[];
- };
可以看到 sds本质上是一个char指针,内部存储结构为一个header+char*. len表示sds实际占用的空间大小, free表示sds尚未使用的空间。buf指向实际的字符串内容。
sizeof(struct sdshsr)在32位操作系统下面是8,redis作者没有用char *buf,是不是觉得这样一个头部就可以节约4字节内存?char buf[]被gcc编译器理解为动态数组了,而且buf变量只能放在结构体最后位置。否则报错。
2, 关于sds的操作
1)创建sds
sds.c有三个函数用于创建sds
- sds sdsnewlen(const void *init, size_t initlen);//主要的创建函数
- sds sdsnew(const char *init);//这个函数实际上调用的sdsnewlen,initlen问 字符串init的大小(不包含最后的‘\0’)
- sds sdsempty();//同调用sdsnewlen,只不过initlen为0
sdsnewlen的具体代码
- sds sdsnewlen(const void *init, size_t initlen) {
- struct sdshdr *sh;
- if (init) {
- sh = zmalloc(sizeof(struct sdshdr)+initlen+1); //一个sds真正的占用空间为头部大小+字符串长度
- } else {
- sh = zcalloc(sizeof(struct sdshdr)+initlen+1);
- }
- if (sh == NULL) return NULL;
- sh->len = initlen; //此处的len,没有把'\0‘计算在内
- sh->free = 0;
- if (initlen && init)
- memcpy(sh->buf, init, initlen);
- sh->buf[initlen] = '\0';
- return (char*)sh->buf; //返回的指针指向真正字符串内容,而不是返回头部指针,这样用户之需要关心真正的内容就行,不需要管理头部
- }
- sds mysds = sdsnewlen("redis", 5);
那么如何获取头部信息呢,比如说我想获取sds的长度(buf长度)
- static inline size_t sdslen(const sds s) {
- struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));//往前偏移
- return sh->len;
- }
2)sds释放
- void sdsfree(sds s) {
- if (s == NULL) return;
- zfree(s-sizeof(struct sdshdr));
- }
3)sds其他操作这里就不再叙述,基本上常见的字符串的操作都可以找到。