// set添加元素的处理函数 , 在文件t_set.c中//过程汇总//检查set是否存在不存在则创建一个set结合 。//根据传入的set集合一个个进行添加 , 添加的时候需要进行内存压缩 。//setTypeAdd执行set添加过程中会判断是否进行编码转换 。void saddCommand(client *c) {robj *set;int j, added = 0;// 取出集合对象set = lookupKeyWrite(c->db,c->argv[1]);// 对象不存在 , 创建一个新的 , 并将它关联到数据库if (set == NULL) {set = setTypeCreate(c->argv[2]->ptr);dbAdd(c->db,c->argv[1],set);}// 对象存在 , 检查类型else {if (set->type != OBJ_SET) {addReply(c,shared.wrongtypeerr);return;}}// 将所有输入元素添加到集合中for (j = 2; j < c->argc; j++) {// set 类型 添加元素if (setTypeAdd(set,c->argv[j]->ptr)) added++;}// 如果有至少一个元素被成功添加 , 那么执行以下程序if (added) {// 发送键修改信号signalModifiedKey(c,c->db,c->argv[1]);// 发送事件通知notifyKeyspaceEvent(NOTIFY_SET,"sadd",c->argv[1],c->db->id);}// 将数据库设为脏server.dirty += added;// 返回添加元素的数量addReplyLongLong(c,added);}//元素已经存在 直接返回 0 , 否则添加元素 返回 1//过程汇总//如果能够转成int的对象(isObjectRepresentableAsLongLong) , 那么就用intset保存 。//如果用intset保存的时候 , 如果长度超过512(REDIS_SET_MAX_INTSET_ENTRIES)就转为hashtable编码 。//其他情况统一用hashtable进行存储 。int setTypeAdd(robj *subject, sds value) {long long llval;// 字典if (subject->encoding == OBJ_ENCODING_HT) {// 将 value 作为键 , NULL 作为值 , 将元素添加到字典中dict *ht = subject->ptr;dictEntry *de = dictAddRaw(ht,value,NULL);if (de) {dictSetKey(ht,de,sdsdup(value));dictSetVal(ht,de,NULL);return 1;}}// intsetelse if (subject->encoding == OBJ_ENCODING_INTSET) {// 判断是否可以用整形编码 , 可以的话用intset 编码if (isSdsRepresentableAsLongLong(value,&llval) == C_OK) {uint8_t success = 0;subject->ptr = intsetAdd(subject->ptr,llval,&success);if (success) {//如果元素个数超过set-max-intset-entries[ 默认 512 ]时 , 将转化为 hashtable 数据结构if (intsetLen(subject->ptr) > server.set_max_intset_entries)setTypeConvert(subject,OBJ_ENCODING_HT);return 1;}} else {//转整形失败 , 直接用hashtable存储setTypeConvert(subject,OBJ_ENCODING_HT);// 执行添加操作serverAssert(dictAdd(subject->ptr,sdsdup(value),NULL) == DICT_OK);return 1;}} else {// 未知编码serverPanic("Unknown set encoding");}return 0;}7.ZSet 数据结构1)介绍
【1】ZSet 为有序的 , 自动去重的集合数据类型 , ZSet 数据结构底层实现为 字典(dict) + 跳表(skiplist) ,当数据比较少时 , 用ziplist编码结构存储 。
zset-max-ziplist-entries128// 元素个数超过128 , 将用skiplist编码zset-max-ziplist-value64//单个元素大小超过 64 byte, 将用 skiplist编码【2】数据比较少时 , 用ziplist编码结构存储的图示:
文章插图
2)skiplist 分析解析
【1】数据结构代码
// 创建zset 数据结构: 字典 + 跳表robj *createZsetObject(void) { zset *zs = zmalloc(sizeof(*zs)); robj *o; // dict用来查询数据到分数的对应关系 , 如 zscore 就可以直接根据 元素拿到分值 zs->dict = dictCreate(&zsetDictType,NULL); // skiplist用来根据分数查询数据(可能是范围查找) zs->zsl = zslCreate(); // 设置对象类型 o = createObject(OBJ_ZSET,zs); // 设置编码类型 o->encoding = OBJ_ENCODING_SKIPLIST; return o;}//位于edis/src/server.h 中#define ZSKIPLIST_MAXLEVEL 32 /* Should be enough for 2^64 elements */#define ZSKIPLIST_P 0.25/* Skiplist P = 1/4 */typedef struct zskiplistNode {sds ele;//存储字符串类型数据 redis3.0版本中使用robj类型表示 , 但是在redis4.0.1中直接使用sds类型表示double score;//存储排序的分值struct zskiplistNode *backward;//指向上一个节点,用于zrevrange命令struct zskiplistLevel {struct zskiplistNode *forward;//指向下一个节点unsigned long span;//到达后一个节点的跨度(两个相邻节点span为1)} level[];//该节点在各层的信息 , 柔性数组成员} zskiplistNode;typedef struct zskiplist {struct zskiplistNode *header, *tail;// 跳跃表头尾节点unsigned long length;//节点个数int level;//除头结点外最大的层数} zskiplist;typedef struct zset {dict *dict;zskiplist *zsl;} zset;
经验总结扩展阅读
- 七 Netty 学习:NioEventLoop 对应线程的创建和启动源码说明
- Spring mvc源码分析系列--Servlet的前世今生
- spring cron表达式源码分析
- 集合框架——LinkedList集合源码分析
- 含源码 手把手教你使用LabVIEW OpenCV DNN实现手写数字识别
- 补充部分---ScheduledThreadPoolExecutor类分析 线程池底层原理详解与源码分析
- 五 Netty 学习:服务端启动核心流程源码说明
- HashMap底层原理及jdk1.8源码解读
- 含源码 手把手教你使用LabVIEW人工智能视觉工具包快速实现传统Opencv算子的调用
- 含源码 手把手教你使用LabVIEW人工智能视觉工具包快速实现图像读取与采集