Redis值集合物件原始碼閱讀
阿新 • • 發佈:2018-11-24
setTypeCreate:返回一個集合物件
robj *setTypeCreate(robj *value) {
if (isObjectRepresentableAsLongLong(value,NULL) == REDIS_OK)
return createIntsetObject();
return createSetObject();
}
setTypeAdd:向集合中新增元素
int setTypeAdd(robj *subject, robj *value) { long long llval; // 字典 if (subject->encoding == REDIS_ENCODING_HT) { // 將 value 作為鍵, NULL 作為值,將元素新增到字典中 if (dictAdd(subject->ptr,value,NULL) == DICT_OK) { incrRefCount(value); return 1; } // intset } else if (subject->encoding == REDIS_ENCODING_INTSET) { // 如果物件的值可以編碼為整數的話,那麼將物件的值新增到 intset 中 if (isObjectRepresentableAsLongLong(value,&llval) == REDIS_OK) { uint8_t success = 0; subject->ptr = intsetAdd(subject->ptr,llval,&success); if (success) { /* Convert to regular set when the intset contains * too many entries. */ // 新增成功 // 檢查集合在新增新元素之後是否需要轉換為字典 if (intsetLen(subject->ptr) > server.set_max_intset_entries) setTypeConvert(subject,REDIS_ENCODING_HT); return 1; } // 如果物件的值不能編碼為整數,那麼將集合從 intset 編碼轉換為 HT 編碼 // 然後再執行新增操作 } else { /* Failed to get integer from object, convert to regular set. */ setTypeConvert(subject,REDIS_ENCODING_HT); /* The set *was* an intset and this value is not integer * encodable, so dictAdd should always work. */ redisAssertWithInfo(NULL,value,dictAdd(subject->ptr,value,NULL) == DICT_OK); incrRefCount(value); return 1; } // 未知編碼 } else { redisPanic("Unknown set encoding"); } // 新增失敗,元素已經存在 return 0; }
setTypeRemove:刪除某個操作
int setTypeRemove(robj *setobj, robj *value) { long long llval; // HT if (setobj->encoding == REDIS_ENCODING_HT) { // 從字典中刪除鍵(集合元素) if (dictDelete(setobj->ptr,value) == DICT_OK) { // 看是否有必要在刪除之後縮小字典的大小 if (htNeedsResize(setobj->ptr)) dictResize(setobj->ptr); return 1; } // INTSET } else if (setobj->encoding == REDIS_ENCODING_INTSET) { // 如果物件的值可以編碼為整數的話,那麼嘗試從 intset 中移除元素 if (isObjectRepresentableAsLongLong(value,&llval) == REDIS_OK) { int success; setobj->ptr = intsetRemove(setobj->ptr,llval,&success); if (success) return 1; } // 未知編碼 } else { redisPanic("Unknown set encoding"); } // 刪除失敗 return 0; }
setTypeIsMember:檢查是否有某個元素
int setTypeIsMember(robj *subject, robj *value) { long long llval; // HT if (subject->encoding == REDIS_ENCODING_HT) { return dictFind((dict*)subject->ptr,value) != NULL; // INTSET } else if (subject->encoding == REDIS_ENCODING_INTSET) { if (isObjectRepresentableAsLongLong(value,&llval) == REDIS_OK) { return intsetFind((intset*)subject->ptr,llval); } // 未知編碼 } else { redisPanic("Unknown set encoding"); } // 查詢失敗 return 0; }
setTypeRandomElement:從集合中隨機獲取一個元素
int setTypeRandomElement(robj *setobj, robj **objele, int64_t *llele) {
if (setobj->encoding == REDIS_ENCODING_HT) {
dictEntry *de = dictGetRandomKey(setobj->ptr);
*objele = dictGetKey(de);
} else if (setobj->encoding == REDIS_ENCODING_INTSET) {
*llele = intsetRandom(setobj->ptr);
} else {
redisPanic("Unknown set encoding");
}
return setobj->encoding;
}
setTypeSize:返回集合中元素的個數
unsigned long setTypeSize(robj *subject) {
if (subject->encoding == REDIS_ENCODING_HT) {
return dictSize((dict*)subject->ptr);
} else if (subject->encoding == REDIS_ENCODING_INTSET) {
return intsetLen((intset*)subject->ptr);
} else {
redisPanic("Unknown set encoding");
}
}
saddCommand:sadd命令
void saddCommand(redisClient *c) {
robj *set;
int j, added = 0;
// 取出集合物件
set = lookupKeyWrite(c->db,c->argv[1]);
// 物件不存在,建立一個新的,並將它關聯到資料庫
if (set == NULL) {
set = setTypeCreate(c->argv[2]);
dbAdd(c->db,c->argv[1],set);
// 物件存在,檢查型別
} else {
if (set->type != REDIS_SET) {
addReply(c,shared.wrongtypeerr);
return;
}
}
// 將所有輸入元素新增到集合中
for (j = 2; j < c->argc; j++) {
c->argv[j] = tryObjectEncoding(c->argv[j]);
// 只有元素未存在於集合時,才算一次成功新增
if (setTypeAdd(set,c->argv[j])) added++;
}
// 如果有至少一個元素被成功新增,那麼執行以下程式
if (added) {
// 傳送鍵修改訊號
signalModifiedKey(c->db,c->argv[1]);
// 傳送事件通知
notifyKeyspaceEvent(REDIS_NOTIFY_SET,"sadd",c->argv[1],c->db->id);
}
// 將資料庫設為髒
server.dirty += added;
// 返回新增元素的數量
addReplyLongLong(c,added);
}
sremCommand:刪除多個值
void sremCommand(redisClient *c) {
robj *set;
int j, deleted = 0, keyremoved = 0;
// 取出集合物件
if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
checkType(c,set,REDIS_SET)) return;
// 刪除輸入的所有元素
for (j = 2; j < c->argc; j++) {
// 只有元素在集合中時,才算一次成功刪除
if (setTypeRemove(set,c->argv[j])) {
deleted++;
// 如果集合已經為空,那麼刪除集合物件
if (setTypeSize(set) == 0) {
dbDelete(c->db,c->argv[1]);
keyremoved = 1;
break;
}
}
}
// 如果有至少一個元素被成功刪除,那麼執行以下程式
if (deleted) {
// 傳送鍵修改訊號
signalModifiedKey(c->db,c->argv[1]);
// 傳送事件通知
notifyKeyspaceEvent(REDIS_NOTIFY_SET,"srem",c->argv[1],c->db->id);
// 傳送事件通知
if (keyremoved)
notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",c->argv[1],
c->db->id);
// 將資料庫設為髒
server.dirty += deleted;
}
// 將被成功刪除元素的數量作為回覆
addReplyLongLong(c,deleted);
}
smoveCommand:將指定元素從源集合移動到目的集合
void smoveCommand(redisClient *c) {
robj *srcset, *dstset, *ele;
// 取出源集合
srcset = lookupKeyWrite(c->db,c->argv[1]);
// 取出目標集合
dstset = lookupKeyWrite(c->db,c->argv[2]);
// 編碼元素
ele = c->argv[3] = tryObjectEncoding(c->argv[3]);
/* If the source key does not exist return 0 */
// 源集合不存在,直接返回
if (srcset == NULL) {
addReply(c,shared.czero);
return;
}
/* If the source key has the wrong type, or the destination key
* is set and has the wrong type, return with an error. */
// 如果源集合的型別錯誤
// 或者目標集合存在、但是型別錯誤
// 那麼直接返回
if (checkType(c,srcset,REDIS_SET) ||
(dstset && checkType(c,dstset,REDIS_SET))) return;
/* If srcset and dstset are equal, SMOVE is a no-op */
// 如果源集合和目標集合相等,那麼直接返回
if (srcset == dstset) {
addReply(c,shared.cone);
return;
}
/* If the element cannot be removed from the src set, return 0. */
// 從源集合中移除目標元素
// 如果目標元素不存在於源集合中,那麼直接返回
if (!setTypeRemove(srcset,ele)) {
addReply(c,shared.czero);
return;
}
// 傳送事件通知
notifyKeyspaceEvent(REDIS_NOTIFY_SET,"srem",c->argv[1],c->db->id);
/* Remove the src set from the database when empty */
// 如果源集合已經為空,那麼將它從資料庫中刪除
if (setTypeSize(srcset) == 0) {
// 刪除集合物件
dbDelete(c->db,c->argv[1]);
// 傳送事件通知
notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",c->argv[1],c->db->id);
}
// 傳送鍵修改訊號
signalModifiedKey(c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[2]);
// 將資料庫設為髒
server.dirty++;
/* Create the destination set when it doesn't exist */
// 如果目標集合不存在,那麼建立它
if (!dstset) {
dstset = setTypeCreate(ele);
dbAdd(c->db,c->argv[2],dstset);
}
/* An extra key has changed when ele was successfully added to dstset */
// 將元素新增到目標集合
if (setTypeAdd(dstset,ele)) {
// 將資料庫設為髒
server.dirty++;
// 傳送事件通知
notifyKeyspaceEvent(REDIS_NOTIFY_SET,"sadd",c->argv[2],c->db->id);
}
// 回覆 1
addReply(c,shared.cone);
}
sismemberCommand:檢查是否存在指定成員
void sismemberCommand(redisClient *c) {
robj *set;
// 取出集合物件
if ((set = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
checkType(c,set,REDIS_SET)) return;
// 編碼輸入元素
c->argv[2] = tryObjectEncoding(c->argv[2]);
// 檢查是否存在
if (setTypeIsMember(set,c->argv[2]))
addReply(c,shared.cone);
else
addReply(c,shared.czero);
}
scardCommand:獲取成員的集合數目
void scardCommand(redisClient *c) {
robj *o;
// 取出集合
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
checkType(c,o,REDIS_SET)) return;
// 返回集合的基數
addReplyLongLong(c,setTypeSize(o));
}
spopCommand:隨機彈出一個集合中的元素
void spopCommand(redisClient *c) {
robj *set, *ele, *aux;
int64_t llele;
int encoding;
// 取出集合
if ((set = lookupKeyWriteOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
checkType(c,set,REDIS_SET)) return;
// 從集合中隨機取出一個元素
encoding = setTypeRandomElement(set,&ele,&llele);
// 將被取出元素從集合中刪除
if (encoding == REDIS_ENCODING_INTSET) {
ele = createStringObjectFromLongLong(llele);
set->ptr = intsetRemove(set->ptr,llele,NULL);
} else {
incrRefCount(ele);
setTypeRemove(set,ele);
}
// 傳送事件通知
notifyKeyspaceEvent(REDIS_NOTIFY_SET,"spop",c->argv[1],c->db->id);
/* Replicate/AOF this command as an SREM operation */
// 將這個命令當作 SREM 來傳播,防止產生有害的隨機性,導致資料不一致
// (不同的伺服器隨機刪除不同的元素)
aux = createStringObject("SREM",4);
rewriteClientCommandVector(c,3,aux,c->argv[1],ele);
decrRefCount(ele);
decrRefCount(aux);
// 返回回覆
addReplyBulk(c,ele);
// 如果集合已經為空,那麼從資料庫中刪除它
if (setTypeSize(set) == 0) {
// 刪除集合
dbDelete(c->db,c->argv[1]);
// 傳送事件通知
notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",c->argv[1],c->db->id);
}
// 傳送鍵修改訊號
signalModifiedKey(c->db,c->argv[1]);
// 將資料庫設為髒
server.dirty++;
}