本文目的:
模仿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函数开始。
下面以myhmset命令执行的时序图来说明具体调用的函数及其所在的文件,其他命令执行方式都类似.图中方框内的文件名,所有关于hash类型的命令都在文件t_hash.c中,所有关于set类型的命令都在文件t_set.c中,其他类型的命令处理函数可以找对应的文件,redis命令非常规范,找起来不难.
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