玩一把redis源码之二——自定义hash、set数据类型

本文目的:

模仿redis实现自定义字典数据结构
模仿redis实现自定义set、hash数据类型

内容简介

redis中set和hash数据类型用到了hash算法,redis中存储数据库所有数据对象使用的基础数据结构就是字典数据结构,set和hash两个数据类型底层实现都用到字典结构,但是针对不同的值类型和键值对数量set与hash还是有很大区别,比如当值都是int时set会使用一个inset结构,当键值对比较少时hash数据域会使用ziplist结构。本文只针对set和hash都采用字典结构的情况,此时set类型基本等同于value数据域为NULL的hash类型。

redis源码比本文描述复杂很多,阅读本文可以了解基本的字典结构和hash算法,对redis源码也会有一个大致认知。

本文的hashcode算法参考的jdk hashmap实现,但是比hashmap简单,能做到一定的随机性。

文档分为三大部分:

  • 环境介绍与效果演示
  • redis代码执行流程
  • 代码实现

环境介绍和效果演示

环境介绍

redis版本为5.0.3 64 bit
操作系统版本为Ubuntu 18.10 64bit
源码可以用gedit查看 gdb调试
ide 可以用eclipse+CDT

效果演示

myhash实现命令以及对应的原生hash命令:

实现命令 redis命令 含义
myhmset hmset 新建hash
myhgetall hgetall 查询所有键值对
myhdel hdel 删除一个或多个field
myhget hget 获取某个field值
myhexists hexists 是否存在指定的field
myhkeys hkeys 返回所有field
myhlen hlen 获取hash键值对数量
myhmget hmget 获取指定一个或多个filed值
myhvals hvals 返回所有的值

myset实现命令以及对应原生set命令:

实现命令 redis命令 含义
mysadd sadd 向set添加一个或多个成员
myscard scard 返回成员数
mysmembers smembers 返回所有成员
myspop spop 移除并返回一个随机元素
mysunion sunion 返回所有集合的并集
mysinter sinter 返回给定集合的交集
mysdiff sdiff 返回差集

演示效果

测试myhash

127.0.0.1:6379> myhmset gjwh1 gjwk1 gjwv1 gjwk2 gjwv2
OK
127.0.0.1:6379> myhlen gjwh1
(integer) 2
127.0.0.1:6379> myhgetall gjwh1
1) "gjwk2"
2) "gjwv2"
3) "gjwk1"
4) "gjwv1"
127.0.0.1:6379> myhget gjwh1 gjwk1
"gjwv1"
127.0.0.1:6379> myhdel gjwh1 gjwk1
(integer) 1
127.0.0.1:6379> myhgetall gjwh1
1) "gjwk2"
2) "gjwv2"
127.0.0.1:6379> mygexists gjwh1 gjwk1
(error) ERR unknown command `mygexists`, with args beginning with: `gjwh1`, `gjwk1`, 
127.0.0.1:6379> myhexists gjwh1 gjwk1
(integer) 0
127.0.0.1:6379> myhexists gjwh1 gjwk2
(integer) 1
127.0.0.1:6379> myhget gjwh1 gjwk2
"gjwv2"
127.0.0.1:6379> myhmset gjwh1 gjwk3 gjwv3 gjwk4 gjwv4
OK
127.0.0.1:6379> myhkeys gjwh1
1) "gjwk4"
2) "gjwk3"
3) "gjwk2"
127.0.0.1:6379> myvals gjwh1
(error) ERR unknown command `myvals`, with args beginning with: `gjwh1`, 
127.0.0.1:6379> myhvals gjwh1
1) "gjwv4"
2) "gjwv3"
3) "gjwv2"

测试myset

127.0.0.1:6379> mysadd gjws1 gjwsk1 gjwsk2 gjwsk3
(integer) 3
127.0.0.1:6379> mysmembers gjws1
1) "gjwsk1"
2) "gjwsk2"
3) "gjwsk3"
127.0.0.1:6379> myscard gjws1
(integer) 3
127.0.0.1:6379> myspop gjws1
"gjwsk1"
127.0.0.1:6379> mysmembers gjws1
1) "gjwsk2"
2) "gjwsk3"
127.0.0.1:6379> myscard gjws1
(integer) 2
127.0.0.1:6379> myspop gjws1
"gjwsk2"
127.0.0.1:6379> mysmembers gjws1
1) "gjwsk3"
127.0.0.1:6379> myscard gjws1
(integer) 1

redis代码执行流程

此部分只选择与本文档相关部分做简单介绍,下面从server.c文件的processCommand函数开始。


11.png

下面以myhmset命令执行的时序图来说明具体调用的函数及其所在的文件,其他命令执行方式都类似.图中方框内的文件名,所有关于hash类型的命令都在文件t_hash.c中,所有关于set类型的命令都在文件t_set.c中,其他类型的命令处理函数可以找对应的文件,redis命令非常规范,找起来不难.


22.png

set 的执行流程与hash类似 ,在此不再赘述。

代码实现

添加头文件mydict.h
头文件中定义了基础的数据结构,需要实现的与数据结构有关的方法。最重要的是hashcode、rehash、还有一个迭代器,其他方法基本都是通常操作字典的基本方法。本文并非实现了全部方法,按照本文介绍的知识,可以比较方便的编写自己的方法。

/*
 * mydict.h
 *
 *  Created on: Jan 16, 2019
 *      Author: gjw
 */

#ifndef SRC_MYDICT_H_
#define SRC_MYDICT_H_
#define mydictSlots(d) ((d)->ht[0].size+(d)->ht[1].size)
#define mydictSize(d) ((d)->ht[0].used+(d)->ht[1].used)
typedef struct mydictEntry {
    void *key;
    void *val;
    struct mydictEntry *next;
} mydictEntry;
typedef struct mydictht {
    mydictEntry **table;
    unsigned long size;
    unsigned long sizemask;
    unsigned long used;
} mydictht;
typedef struct mydict {
    //dictType *type;
   // void *privdata;
    mydictht ht[2];
    long rehashidx; /* rehashing not in progress if rehashidx == -1 */
   // unsigned long iterators; /* number of iterators currently running */
} mydict;
typedef struct mydictIterator{
    mydict * myd;
    mydictEntry *next;
    int tableNum;
    int index;
}mydictIterator;

/* API */
mydict *mydictCreate();
int mydictExpand(mydict *d, unsigned long size);
int mydictAdd(mydict *d, void *key, void *val);
mydictEntry * doMydictIter(mydictIterator * iter);
mydictIterator * getMydictIterator(mydict *d);
//mydictEntry *dictAddRaw(mydict *d, void *key, mydictEntry **existing);
//mydictEntry *dictAddOrFind(mydict *d, void *key);
int mydictReplace(mydict *d, void *key, void *val);
int mydictDelete(mydict *d, const void *key);
//mydictEntry *dictUnlink(mydict *ht, const void *key);
void mydictFreeUnlinkedEntry(mydict *d, mydictEntry *he);
void mydictRelease(mydict *d);
mydictEntry * mydictFind(mydict *d, const void *key);
void *mydictFetchValue(mydict *d, const void *key);
int mydictResize(mydict *d);
mydictEntry *mydictGetRandomKey(mydict *d);
unsigned int mydictGetSomeKeys(mydict *d, mydictEntry **des, unsigned int count);
int mydictGenHashFunction(const void *key, int len);
int mydictGenCaseHashFunction(const unsigned char *buf, int len);
void mydictEmpty(mydict *d, void(callback)(void*));
void mydictEnableResize(void);
void mydictDisableResize(void);
int mydictRehash(mydict *d, int n);
int mydictRehashMilliseconds(mydict *d, int ms);
void mydictSetHashFunctionSeed(int *seed);
int *mydictGetHashFunctionSeed(void);
int mydictGetHash(mydict *d, const void *key);
//mydictEntry **dictFindEntryRefByPtrAndHash(mydict *d, const void *oldptr, int hash);
mydictEntry *mydictGetRandomKey(mydict *d);

#endif /* SRC_MYDICT_H_ */

mydict.c 实现具体的方法

/*
 * mydict.c
 *
 *  Created on: Jan 16, 2019
 *      Author: gjw
 */

#include "mydict.h"
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "sds.h"
unsigned int hashCode(char * key) {
    int len = strlen(key);
    unsigned int hash = 0;
    for (int i = 0; i < len; i++) {
        hash = 31 * hash + (key[i]);
    }
    return hash ^ (hash >> 16);

}
static int _myinitht(mydictht * ht) {
    ht->table = NULL;
    ht->size = 0UL;
    ht->sizemask = 0UL;
    ht->used = 0UL;
    return 0;

}
mydict *mydictCreate() {
    mydict * d = (mydict *) malloc(sizeof(*d));
    _myinitht(&d->ht[0]);
    _myinitht(&d->ht[1]);
    d->rehashidx = -1;
    return d;
}
unsigned long _mydictNextPower(unsigned long size) {
    int i = 4;
    while (1) {
        if (i >= size)
            return i;
        i = i * 2;
    }
}
int mydictExpand(mydict *d, unsigned long size) {
    int realSize = _mydictNextPower(size);
//    int index=d->ht[0].size==0?0:1;
    if (realSize <= d->ht[0].size)
        return 1;
    mydictEntry ** entries = (mydictEntry **) malloc(
            realSize * sizeof(mydictEntry *));
    d->ht[1].table = entries;
    d->ht[1].size = realSize;
    d->ht[1].sizemask = realSize - 1;
    d->ht[1].used = 0;
    d->rehashidx = 0;
    for (int i = 0; i < realSize; i++) {
        d->ht[1].table[i] = NULL;
    }
    return 0;
}
//return bucket index
int _myKeyIndex(mydict *d, void *key, unsigned long hash) {
    int index = 0, len1 = sdslen(key), len2 = 0;
    int i;

    for (i = 0; i <= 1; i++) {
        if (d->ht[i].size == 0)
            continue;
        index = hash % d->ht[i].size;
        mydictEntry * e = d->ht[i].table[index];
        if (!e)
            break;
        len2 = sdslen(e->key);
        if (len1 == len2) {
            if (memcmp(e->key, key, len1) == 0) {
                return -1;
            }
        }
        if (d->rehashidx == -1)
            break;
    }
    return index;

}
int _mydictExpandIfNeeded(mydict *d) {
    /* Incremental rehashing already in progress. Return. */
    if (d->rehashidx != -1){
        mydictRehash(d,100);
        return 0;}
    if (d->ht[0].size == 0)
        return mydictExpand(d, 4);
    if (d->ht[0].used >= d->ht[0].size && (d->ht[0].used / d->ht[0].size > 2)) {
        return mydictExpand(d, d->ht[0].used * 2);
    }
    return 0;
}

int mydictAdd(mydict *d, void *key, void *val) {
    unsigned long hash = hashCode(key);
    _mydictExpandIfNeeded(d);
    int index = _myKeyIndex(d, key, hash);
    if (index == -1)
        return 1;
    mydictht *ht = (d->rehashidx == -1 ? &d->ht[0] : &d->ht[1]);
    mydictEntry * n = (mydictEntry *) malloc(sizeof(mydictEntry));
    n->key=key;
    n->val=val;
    n->next = ht->table[index];
    ht->table[index] = n;
    ht->used++;
    return 0;
}
//mydictEntry *dictAddRaw(mydict *d, void *key, mydictEntry **existing);
//mydictEntry *dictAddOrFind(mydict *d, void *key);
//int mydictReplace(mydict *d, void *key, void *val);
void _mydestroyDictEntry(mydictEntry * e) {
    mydictEntry * e1 = e;
    while (e) {
        sdsfree(e->key);
        sdsfree(e->val);
        e = e->next;
        e1->next = NULL;
        free(e1);
    }
}
int mydictDelete(mydict *d, const void *key) {
    unsigned long hash = hashCode(key);
    int index = 0;
    int len1 = 0, len2 = 0;
    mydictEntry * n;
    mydictEntry * previous;
    mydictEntry * head;
    if (index == -1)
        return 1;
    for (int i = 0; i <= 1; i++) {
        if(d->ht[i].size==0)continue;
        index = hash % d->ht[i].size;
        head = d->ht[i].table[index];
        previous = head;
        n = head;
        while (n) {
            len1 = sdslen(n->key);
            len2 = sdslen(key);
            if (len1 == len2 && memcmp(n->key, key, len1) == 0) {
                if (n == head) {
                    d->ht[i].table[index] = NULL;
                    _mydestroyDictEntry(head);
                } else {
                    previous->next = n->next;
                    _mydestroyDictEntry(n);
                }
                d->ht[i].used--;
                _mydictExpandIfNeeded(d);
                return 0;
            }
            previous = n;
            n = n->next;
        }
        if (d->rehashidx == -1)
            break;
    }
    return 1;

}
//mydictEntry *dictUnlink(mydict *ht, const void *key);
//void mydictFreeUnlinkedEntry(mydict *d, mydictEntry *he);
void mydictRelease(mydict *d) {
    int size = 0;
    for (int i = 0; i <= 1; i++) {
        size = d->ht[i].size;
        for (int j = 0; j < size; j++) {
            _mydestroyDictEntry(d->ht[i].table[j]);
            d->ht[i].used--;
        }
        if (d->rehashidx == -1)
            break;
    }
free(d);
}
mydictEntry * mydictFind(mydict *d, const void *key) {
    unsigned int hash = hashCode(key);
    int index, len1, len2;
    mydictEntry *e;
    for (int i = 0; i <= 1; i++) {
        if(d->ht[i].size==0)continue;
        index = hash % d->ht[i].size;
        e = d->ht[i].table[index];
        while (e) {
            len1 = sdslen(e->key);
            len2 = sdslen(key);
            if (len1 == len2 && memcmp(e->key, key, len1) == 0)
                return e;
            e = e->next;
        }
        return NULL;
    }

}
int mydictRehash(mydict *d, int n) {
    int emptySize = n * 10;
    int rehashinx = d->rehashidx;
    int inx;
    mydictEntry *e, *next;
    while (d->ht[0].size!=0&&d->ht[0].table[rehashinx] == NULL) {
        rehashinx++;
        d->rehashidx++;
        d->ht[0].size--;
    }
    while (n--) {
        if (d->ht[0].size == 0) {
            d->rehashidx = -1;
            d->ht[0] = d->ht[1];
            _myinitht(&d->ht[1]);
            break;
        }
        e = d->ht[0].table[rehashinx];
        while (e) {
            inx = hashCode(e->key) % (d->ht[1].size);
            next = d->ht[1].table[inx];
            e->next = next;
            d->ht[1].table[inx] = e;
            d->ht[1].used++;
            d->ht[0].used--;
            e = e->next;
        }
        d->ht[0].size--;
        rehashinx++;
        d->rehashidx = rehashinx;
    }
    return 0;
}
int mydictGetHash(mydict *d, const void *key) {
    return hashCode(key);
}
int mydictRehashMilliseconds(mydict *d, int ms) {
    long long start = timeInMilliseconds();
    int rehashes = 0;

    while (mydictRehash(d, 100)) {
        rehashes += 100;
        if (timeInMilliseconds() - start > ms)
            break;
    }
    return rehashes;
}
int mydictResize(mydict *d) {
    int minimal;

    if (d->rehashidx != -1)
        return 1;
    minimal = d->ht[0].used;
    if (minimal < 4)
        minimal = 4;
    return mydictExpand(d, minimal);
}
mydictIterator * getMydictIterator(mydict *d) {
    mydictIterator * mi = (mydictIterator *) malloc(sizeof(mydictIterator));
    mi->index = 0;
    mi->myd = d;
    mi->tableNum = 0;
    mi->next = NULL;
    return mi;
}

mydictEntry * doMydictIter(mydictIterator * iter) {
    mydictEntry *e;
    e = iter->next;
    int size = iter->myd->ht[0].size;
    int htindex = iter->index >= size ? iter->index - size : iter->index;
    while (!e) {
        if (iter->index >= mydictSlots(iter->myd)) {
            free(iter);
            return e;
        }
        if (iter->index >= iter->myd->ht[0].size) {
            iter->tableNum = 1;
        }
        e = iter->myd->ht[iter->tableNum].table[htindex];
        htindex++;
        iter->index++;

    }

    iter->next = e->next;
    return e;
}
mydictEntry *mydictGetRandomKey(mydict *d){
    if(mydictSize(d)==0)return NULL;
    while(d->rehashidx!=-1)mydictRehash(d,100);
    int index=random() % d->ht[0].size;
    mydictEntry *e =d->ht[0].table[index];
    mydictEntry *n;
    while(!e){
        index=random() % d->ht[0].size;
        e =d->ht[0].table[index];
    }
    n=e;
    int llen=0;
    while(n){
        n=n->next;
        llen++;
    }
    int pos=random()%llen;
    for(int i=0;i<pos;i++){
        e=e->next;
    }
    return e;
}

在server.c添加字符串——执行函数映射

{"myhmset",myhsetCommand,-4,"wmF",0,NULL,1,1,1,0,0},
    {"myhset",myhsetCommand,-4,"wmF",0,NULL,1,1,1,0,0},
        {"myhdel",myhdelCommand,-3,"wF",0,NULL,1,1,1,0,0},
    {"myhget",myhgetCommand,3,"rF",0,NULL,1,1,1,0,0},
        {"myhlen",myhlenCommand,2,"rF",0,NULL,1,1,1,0,0},
        {"myhkeys",myhkeysCommand,2,"rS",0,NULL,1,1,1,0,0},
        {"myhvals",myhvalsCommand,2,"rS",0,NULL,1,1,1,0,0},
        {"myhgetall",myhgetALLCommand,2,"rR",0,NULL,1,1,1,0,0},
        {"myhexists",myhexistsCommand,3,"rF",0,NULL,1,1,1,0,0},
    {"myhmget",myhmgetCommand,-3,"rF",0,NULL,1,1,1,0,0},
     {"mysadd",mysaddCommand,-3,"wmF",0,NULL,1,1,1,0,0},
     {"myscard",myscardCommand,2,"rF",0,NULL,1,1,1,0,0},
    {"mysmembers",mysmembersCommand,2,"rS",0,NULL,1,1,1,0,0},
    {"myspop",myspopCommand,-2,"wRF",0,NULL,1,1,1,0,0},

在server.h中添加数据类型操作方法声明

#include "mydict.h"  /* mydict ,guojiagnwei add */
#define MYOBJ_HASH 10      /* MYHASH object. */
#define MYOBJ_SET 9      /* MYSET object. */
void myhgetALLCommand(client *c) ;
void myhsetCommand(client *c);
void myhkeysCommand(client *c) ;
void myhvalsCommand(client *c);
void myhdelCommand(client *c);
void myhgetCommand(client *c);
void myhexistsCommand(client *c);
void myhlenCommand(client *c);
void myhmgetCommand(client *c);
void mysaddCommand(client *c);
void myscardCommand(client *c);
void mysmembersCommand(client *c);
void myspopCommand(client *c);
void mysunionCommand(client *c);
void mysinterCommand(client *c);
void mysdiffCommand(client *c);

在object.c中添加创建类型方法

robj *createMyHashObject(void) {
    mydict  *myd = mydictCreate();
    robj *o = createObject(MYOBJ_HASH, myd);
    o->encoding = OBJ_ENCODING_ZIPLIST;
    return o;
}

在t_hash.c中实现server.h声明的操作函数

void myhsetCommand(client *c) {
    int i, created = 0;
    robj *o;

    if ((c->argc % 2) == 1) {
        addReplyError(c, "wrong number of arguments for HMSET");
        return;
    }
    o = lookupKeyWrite(c->db, c->argv[1]);
    if (o == NULL) {
        o = createMyHashObject();
        dbAdd(c->db, c->argv[1], o);
    } else {
        if (o->type != MYOBJ_HASH) {
            addReply(c, shared.wrongtypeerr);
            return;
        }
    }
//    if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
//    hashTypeTryConversion(o,c->argv,2,c->argc-1);

    for (i = 2; i < c->argc; i += 2)
        created += !mydictAdd(o->ptr, sdsdup(c->argv[i]->ptr), sdsdup(c->argv[i + 1]->ptr));

    /* HMSET (deprecated) and HSET return value is different. */
    char *cmdname = c->argv[0]->ptr;
    if (cmdname[1] == 's' || cmdname[1] == 'S') {
        /* HSET */
        addReplyLongLong(c, created);
    } else {
        /* HMSET */
        addReply(c, shared.ok);
    }
    signalModifiedKey(c->db, c->argv[1]);
    notifyKeyspaceEvent(NOTIFY_HASH, "hset", c->argv[1], c->db->id);
    server.dirty++;
}
void myGenerichgetALLCommand(client *c, int flag) {
    robj *o;
    int multiplier = 0;
    int length, count = 0;

    if ((o = lookupKeyReadOrReply(c, c->argv[1], shared.emptymultibulk)) == NULL
            || checkType(c, o, MYOBJ_HASH))
        return;

    length = mydictSize((const mydict* )o->ptr);
    if (flag == 3)
        length = length * 2;
    addReplyMultiBulkLen(c, length);
    mydictIterator *hi = getMydictIterator(o->ptr);
    mydictEntry *e = doMydictIter(hi);
    int len1=0,len2;
    while (e) {

        if (flag & OBJ_HASH_KEY) {
            len1=sdslen(e->key);
            addReplyBulkCBuffer(c, e->key, len1);
            count++;
        }
        if (flag & OBJ_HASH_VALUE) {
            len2=sdslen(e->val);
            addReplyBulkCBuffer(c, e->val, len2);
            count++;
        }

        e = doMydictIter(hi);
    }
    serverAssert(count == length);
}
void myhgetALLCommand(client *c) {
    myGenerichgetALLCommand(c, OBJ_HASH_VALUE | OBJ_HASH_KEY);
}
void myhkeysCommand(client *c) {
    myGenerichgetALLCommand(c,  OBJ_HASH_KEY);
}
void myhvalsCommand(client *c) {
    myGenerichgetALLCommand(c, OBJ_HASH_VALUE);
}
void myhdelCommand(client *c) {
    robj *o;
    int j, deleted = 0, keyremoved = 0;

    if ((o = lookupKeyWriteOrReply(c, c->argv[1], shared.czero)) == NULL
            || checkType(c, o, MYOBJ_HASH))
        return;

    for (j = 2; j < c->argc; j++) {
        if (!mydictDelete(o->ptr, c->argv[j]->ptr)) {
            deleted++;
            if (mydictSize((const mydict*)o->ptr) == 0) {
                dbDelete(c->db, c->argv[1]);
                keyremoved = 1;
                break;
            }
        }
    }
    if (deleted) {
        signalModifiedKey(c->db, c->argv[1]);
        notifyKeyspaceEvent(NOTIFY_HASH, "hdel", c->argv[1], c->db->id);
        if (keyremoved)
            notifyKeyspaceEvent(NOTIFY_GENERIC, "del", c->argv[1], c->db->id);
        server.dirty += deleted;
    }
    addReplyLongLong(c, deleted);
}

void myhgetCommand(client *c) {
    robj *o;

    if ((o = lookupKeyReadOrReply(c, c->argv[1], shared.nullbulk)) == NULL
            || checkType(c, o, MYOBJ_HASH))
        return;
    mydictEntry * me = mydictFind(o->ptr, c->argv[2]->ptr);
    if (me)
        addReplyBulkCBuffer(c, me->val, sdslen(me->val));
    else
        addReply(c, shared.nullbulk);
}

void myhexistsCommand(client *c) {
    robj *o;
    if ((o = lookupKeyReadOrReply(c, c->argv[1], shared.czero)) == NULL
            || checkType(c, o, MYOBJ_HASH))
        return;
    addReply(c, mydictFind(o->ptr, c->argv[2]->ptr) ? shared.cone : shared.czero);
}
void myhlenCommand(client *c){
    robj *o;
    if ((o = lookupKeyReadOrReply(c, c->argv[1], shared.czero)) == NULL
            || checkType(c, o, MYOBJ_HASH))
        return;
    addReplyLongLong(c, mydictSize((const mydict*)o->ptr));
}
void myhmgetCommand(client *c){
    robj *o;
    o = lookupKeyRead(c->db, c->argv[1]);
    if (o != NULL && o->type != OBJ_HASH) {
        addReply(c, shared.wrongtypeerr);
        return;
    }
    addReplyMultiBulkLen(c, c->argc - 2);
    for (int i = 2; i < c->argc; i++) {
        mydictEntry * me = mydictFind(o->ptr, c->argv[i]->ptr);
        if (me == NULL)
            addReply(c, shared.nullbulk);
        else
            addReplyBulkCBuffer(c, me->val, sdslen(me->val));
    }
}

在t_set.c中添加set相关的处理函数,本例为求简洁,用dict实现set类型,重用了几个t_hash中的函数


void mysaddCommand(client *c){
    robj *set;
    int j, added = 0;

    set = lookupKeyWrite(c->db,c->argv[1]);
    if (set == NULL) {
        set = createMyHashObject();
        dbAdd(c->db,c->argv[1],set);
    } else {
        if (set->type != MYOBJ_HASH) {
            addReply(c,shared.wrongtypeerr);
            return;
        }
    }

    for (j = 2; j < c->argc; j++) {
        if (!mydictAdd(set->ptr,sdsdup(c->argv[j]->ptr),NULL)) added++;
    }
    if (added) {
        signalModifiedKey(c->db,c->argv[1]);
        notifyKeyspaceEvent(NOTIFY_SET,"sadd",c->argv[1],c->db->id);
    }
    server.dirty += added;
    addReplyLongLong(c,added);
}
void myscardCommand(client *c){
    myhlenCommand(c);
}
void mysmembersCommand(client *c){
    myhkeysCommand(c);
}
void myspopCommand(client *c){
    robj *set;

    if (c->argc == 3) {
        spopWithCountCommand(c);
        return;
    } else if (c->argc > 3) {
        addReply(c,shared.syntaxerr);
        return;
    }

    /* Make sure a key with the name inputted exists, and that it's type is
     * indeed a set */
    if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
        checkType(c,set,MYOBJ_HASH)) return;

    mydictEntry * e=mydictGetRandomKey(set->ptr);
    /* Remove the element from the set */
    /* Add the element to the reply */
//    addReplyBulk(c,(e->key));
    addReplyBulkCBuffer(c, e->key, sdslen(e->key));
    if (e) {
        mydictDelete(set->ptr,e->key);
    }

    notifyKeyspaceEvent(NOTIFY_SET,"spop",c->argv[1],c->db->id);

    /* Delete the set if it's empty */
    if (mydictSize((mydict *)set->ptr) == 0) {
        dbDelete(c->db,c->argv[1]);
        notifyKeyspaceEvent(NOTIFY_GENERIC,"del",c->argv[1],c->db->id);
    }

    /* Set has been modified */
    signalModifiedKey(c->db,c->argv[1]);
    server.dirty++;

}

上一篇文章没有贴出Makefile的修改,这里一并补上

REDIS_SERVER_OBJ=mydict.o mylinkedlist.o adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o redis-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o lolwut.o lolwut5.o

另外需要修改Makefile.dep

mydict.o: mydict.c mydict.h sds.h
mylinkedlist.o: mylinkedlist.c mylinkedlist.h
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容