首页 > 其他 > 详细

redis底层数据结构之adlist

时间:2016-05-08 01:17:48      阅读:224      评论:0      收藏:0      [点我收藏+]

最近,我想通过redis的源码来学习redis。虽然平时工作中用得不多,不过对redis还是比较感兴趣的,毕竟它的性能是不错的。redis是一个开源的项目,我们可以通过源代码去了解redis。我后面会通过自己的学习,写一些关于redis源码的帖子。帖子的主要内容是分析代码设计,而并不会对源码进行详细解说。如果有不对的地方,请指正。源码是reids 3.0.3版本。


adlist


一、adlist,双链列表


数据结构定义如下:

//节点
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);//复制函数
    void (*free)(void *ptr);//销毁函数
    int (*match)(void *ptr, void *key);//匹配函数
    unsigned long len;
} list;


跟一般的双向链表差别不大。redis的双向链表是设计成一个比较通用的链表,所以value是指针,不限value的实际类型,另外给链表提供了用于复制、销毁、匹配的函数,使得链表的value可支持不同的内存管理方法,或者进行一些回调。


另外,还提供了迭代器,链表的迭代器通常都不是随机迭代器,只能一个元素一个元素地跳。由于这是双向链表,所以迭代器可以向前跳,也可以向后跳,不过redis只提供了向后跳的函数。迭代器也支持正向、反向,具体需要通过direction字段来指定方向。结合direction也可以实现迭代器向前跳,大概是这样adlist便没有提供向前跳的函数吧。


二、adlist的相关操作函数


主要有两类,一类是宏,一类是函数,看代码都比较简单,这里不对代码进行详解。只是简单列举一下函数。

/* Prototypes */
list *listCreate(void);//创建链表
void listRelease(list *list);//销毁链表
list *listAddNodeHead(list *list, void *value);//往表头插入元素
list *listAddNodeTail(list *list, void *value);//往表尾插入元素
list *listInsertNode(list *list, listNode *old_node, void *value, int after); //插入到指定位置
void listDelNode(list *list, listNode *node);//删除节点
listIter *listGetIterator(list *list, int direction);//迭代器
listNode *listNext(listIter *iter);//迭代器向后跳,下一个
void listReleaseIterator(listIter *iter);//销毁迭代器
list *listDup(list *orig);//复制链表
listNode *listSearchKey(list *list, void *key);//查询
listNode *listIndex(list *list, long index);//获取指定位置的节点
void listRewind(list *list, listIter *li);//迭代器返回表头,正向迭代器
void listRewindTail(list *list, listIter *li);//迭代器到表尾,反向迭代器
void listRotate(list *list);//旋转链表,把表尾元素放到表头


三、adlist提供对象的内存管理


这里讲一下adlist如何通过dup,free进行内存管理。


有三个地方需要注意的:

a. 插入元素时,node中的value直接赋值为入参的value,不会调用dup函数。

b. listDup中,如果dup函数存在,调用dup函数去复制value。

c. listDelNode和listRelease中,如果free函数存在,通过free方法去销毁value。


通过上面三点,可以实现一些简单的内存管理方法,主要是对象内存的申请和销毁方面的管理。下面介绍一下简单的用法。


1) adlist不负责申请和销毁


如下面代码(代码没有通过编译测试,可能有误)

char s[] = “hello world”;
list *lst = listCreate();
//list的free,dup函数为空
listAddNodeTail(lst, &s[0]);
listAddNodeTail(lst, &s[1]);
listAddNodeTail(lst, &s[2]);
listAddNodeTail(lst, &s[3]);

//do some thing with lst
list *lst2 = listDup(lst);
//do some thing with lst2

listRelease(lst);
listRelease(lst2);


上面例子中,adlist不负责value内存空间的申请和销毁,完全由调用方管理。这时要求value所指定的元素的生存周期要比adlist的长,否则adlist中的value指定可能会失效。


2) adlist负责动态申请和销毁


如下面代码,(代码没有通过编译测试,可能有误):

void addStringToListHead(list *lst, char *s)
{
    char *snew = (char*)malloc(strlen(s)+1);
    assert(snew);
    strcpy(snew,s);
    listAddNodeHead(list,snew);
}
void *stringDup(void *s)
{
    char *sorg = (char*)s;
    char *snew = (char*)malloc(strlen(sorg)+1);
    assert(snew);
    strcpy(snew,sorg);
    return snew;    
}
void *stringFree(void *s)
{
    if (s) free(s);
}
int stringMatch(void *s1, void *s2)
{
    if (s1 && s2) return strcmp((char*)s1,(char*)s2) == 0 ? 1 : 0;
    if (!s1 && !s2) return 1;
    return 0;
}

list *lst = listCreate();
listSetDupMethod(lst, stringDup);
listSetFreeMethod(lst, stringFree);
listSetMatchMethod(lst, stringMatch);
addStringToListHead(lst,”hello”);
addStringToListHead(lst,”world”);

// do something
list *lst2 = listDup(lst);
// do something

listRelease(lst);
listRelease(lst2);


上面例子中动态生成的空间插入到adlist后,完全由adlist来负责管理,调用方不应该再长期持有或者删除动态空间的指针,否则会造成管理混乱。需要注意的是,adlist的dup函数需在复制时进行深度复制,否则一个动态申请的空间同时被两个指针长期保存,只要其中一个指针被free后,另一个指针就失效,再次访问或者free失效指针都将出错。


3) 未能友好地支持reference counting


未能友好支持reference counting的原因在于插入元素时,没有调用相关函数以增加引用计数。在复制list时通过dup来实现引用计数加1,在删除节点时通过free进行引用计数减1。但是插入元素时由于没有调用dup或是进行其他函数使引用计数加1,value便持有了指针,这使adlist不能友好地支持reference counting。不过这是可以通过其他方式实现支持的,如在插入前进行引用计数器加1,如果插入失败则引用计数减1。


四、容易产生内存碎片。


先不考虑value,只看node。可以看到,adlist进行插入元素或是复制链表时都需要申请节点空间,删除节点时需要释放空间。对于不断地进行插入和删除,如果所有的zmalloc、zfree函数没能作出很好的内存管理策略,很容易造成内容碎片。


五、总结


除了在内存管理上提供了调用方自定义的方法外,adlist跟一般的双向链表没有太大的区别,其特点就如上面所述,这里不再介绍了。

本文出自 “chhquan” 博客,请务必保留此出处http://chhquan.blog.51cto.com/1346841/1771106

redis底层数据结构之adlist

原文:http://chhquan.blog.51cto.com/1346841/1771106

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!