学习完 t_string.c、t_list.c文件后,现在开始学习 t_hash.c 的代码,从文件名可以看到是相关hash的相关命令代码。

1 hsetCommand

1.1 方法说明

   对一个hash键,设置一个键值对。

1.2 命令实践


新增成功返回1,修改返回0

1.3 方法源代码

void hsetCommand(redisClient *c) {int update;robj *o;//获取键对象,如果不存在就创建一个if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return;//判断数据结构是否要转换hashTypeTryConversion(o,c->argv,2,3);//判断是否需要编码转换hashTypeTryObjectEncoding(o,&c->argv[2], &c->argv[3]);//设置键值对update = hashTypeSet(o,c->argv[2],c->argv[3]);//响应结果addReply(c, update ? shared.czero : shared.cone);//标记键被修改signalModifiedKey(c->db,c->argv[1]);//通知事件notifyKeyspaceEvent(REDIS_NOTIFY_HASH,"hset",c->argv[1],c->db->id);//状态变更值递增server.dirty++;
}

1.4 相关方法代码

1.4.1 hashTypeLookupWriteOrCreate

   获取键对象或者创建一个对象

robj *hashTypeLookupWriteOrCreate(redisClient *c, robj *key) {//获取键对象robj *o = lookupKeyWrite(c->db,key);//如果不在创建一个对象,并将键值加入库中if (o == NULL) {o = createHashObject();dbAdd(c->db,key,o);}//如果对象类型不是Hash类型,则返回类型错误 else {if (o->type != REDIS_HASH) {addReply(c,shared.wrongtypeerr);return NULL;}}//返回键对象return o;
}

1.4.2 hashTypeTryConversion

   判断数据结构是否要转变

/* Check the length of a number of objects to see if we need to convert a* ziplist to a real hash. Note that we only check string encoded objects* as their string length can be queried in constant time. */
void hashTypeTryConversion(robj *o, robj **argv, int start, int end) {int i;//如果不是压缩表直接返回if (o->encoding != REDIS_ENCODING_ZIPLIST) return;//遍历键和值//如果是字符串,并且长度超过配置,则进行数据结构幻化for (i = start; i <= end; i++) {if (sdsEncodedObject(argv[i]) &&sdslen(argv[i]->ptr) > server.hash_max_ziplist_value){hashTypeConvert(o, REDIS_ENCODING_HT);break;}}
}

1.4.3 hashTypeTryObjectEncoding

   对参数进行编码

/* Encode given objects in-place when the hash uses a dict. */
void hashTypeTryObjectEncoding(robj *subject, robj **o1, robj **o2) {//如果数据结构是哈希表,则需要进行对象编码if (subject->encoding == REDIS_ENCODING_HT) {if (o1) *o1 = tryObjectEncoding(*o1);if (o2) *o2 = tryObjectEncoding(*o2);}
}

1.4.4 hashTypeSet

   设置键值对

/* Add an element, discard the old if the key already exists.* Return 0 on insert and 1 on update.* This function will take care of incrementing the reference count of the* retained fields and value objects. */
int hashTypeSet(robj *o, robj *field, robj *value) {//更新标记int update = 0;//如果数据结构是压缩表if (o->encoding == REDIS_ENCODING_ZIPLIST) {unsigned char *zl, *fptr, *vptr;//获取键和值field = getDecodedObject(field);value = getDecodedObject(value);zl = o->ptr;//获取压缩表头部位置fptr = ziplistIndex(zl, ZIPLIST_HEAD);if (fptr != NULL) {fptr = ziplistFind(fptr, field->ptr, sdslen(field->ptr), 1);if (fptr != NULL) {/* Grab pointer to the value (fptr points to the field) */vptr = ziplistNext(zl, fptr);redisAssert(vptr != NULL);//标记为更新状态update = 1;/* Delete value *///删除原来的值zl = ziplistDelete(zl, &vptr);/* Insert new value *///插入新的值zl = ziplistInsert(zl, vptr, value->ptr, sdslen(value->ptr));}}//如果是新增if (!update) {/* Push new field/value pair onto the tail of the ziplist *///插入键和值zl = ziplistPush(zl, field->ptr, sdslen(field->ptr), ZIPLIST_TAIL);zl = ziplistPush(zl, value->ptr, sdslen(value->ptr), ZIPLIST_TAIL);}o->ptr = zl;decrRefCount(field);decrRefCount(value);/* Check if the ziplist needs to be converted to a hash table *///检查hash的元素个数是否超过配置,如果超过则转换数据结构为哈希表if (hashTypeLength(o) > server.hash_max_ziplist_entries)hashTypeConvert(o, REDIS_ENCODING_HT);} else if (o->encoding == REDIS_ENCODING_HT) {if (dictReplace(o->ptr, field, value)) { /* Insert */incrRefCount(field);} else { /* Update */update = 1;}incrRefCount(value);} else {redisPanic("Unknown hash encoding");}return update;
}

1.4.5 hashTypeLength

  获取哈希的元素个数

/* Return the number of elements in a hash. */
unsigned long hashTypeLength(robj *o) {unsigned long length = ULONG_MAX;//如果是压缩表if (o->encoding == REDIS_ENCODING_ZIPLIST) {length = ziplistLen(o->ptr) / 2;}//如果是哈希表 else if (o->encoding == REDIS_ENCODING_HT) {length = dictSize((dict*)o->ptr);} else {redisPanic("Unknown hash encoding");}return length;
}

1.5 代码理解

  这次没有和之前一样,一个方法一个方法的来介绍,而是围绕hsetCommand这个方法,从头到尾介绍了下里面出现的方法,这样就能串联在一起知道这个方法整体的细节,先来看下整体的流程。

  1. 获取键对象,如果不存在就创建一个。
  2. 根据键和值的数据长度来判断数据结构是否要转换。
  3. 判断键和值两个参数是否要进行编码动作。
  4. 调用 hashTypeSet 这个方法来设置键值对,并返回更新状态。
  5. 响应更新新增状态结果 。
  6. 标记键被修改。
  7. 变更状态递增。

  通过源代码我们可以知道,hash也是由两种数据结构实现的,一种是我们之前了解过的压缩表,另一种是哈希表。
  默认也是用压缩表来实现哈希表,在设置键值对的时候会检查是否要进行转换,判断条件有两个,一个是判断键和值的字符长度是否超过一定长度,一个是判断hash元素的个数是否超过一定的数量。
  在使用压缩表插入键值对的时候,可以看到键和值都是被当成一个压缩表节点先后从尾部插入进去。

2 hmsetCommand

2.1 方法说明

  对一个hash键,一次设置多个键值对。

2.2 命令实践

2.3 方法源代码

void hmsetCommand(redisClient *c) {int i;robj *o;//校验参数数量是否为偶数if ((c->argc % 2) == 1) {addReplyError(c,"wrong number of arguments for HMSET");return;}//获取hash键对象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) {hashTypeTryObjectEncoding(o,&c->argv[i], &c->argv[i+1]);hashTypeSet(o,c->argv[i],c->argv[i+1]);}//响应okaddReply(c, shared.ok);//标记键被修改signalModifiedKey(c->db,c->argv[1]);//通知事件notifyKeyspaceEvent(REDIS_NOTIFY_HASH,"hset",c->argv[1],c->db->id);server.dirty++;
}

2.4 代码理解

  1. 校验参数数量是否为偶数。
  2. 获取hash键对象。
  3. 根据所有键和值,判断是否进行数据结构转换。
  4. 遍历所有键和值,调用 hashTypeSet 进行键值对写入。
  5. 响应ok。
  6. 标记键被修改,触发通知事件。

3 总结

  1. Redis中hash 是两种数据结构实现的,一种是压缩表,一种是哈希表。
  2. hset、hmset命令会判断每次设置的值是否会引起数据结构转换。
  3. hset 在设置键值对的时候,如果没有会先新建一个键值对。
  4. hset 新建和更新的时候返回值不同。

Redis源码学习(10),t_hash.c 学习(一),hset、hmset 命令学习相关推荐

  1. Redis源码解析(15) 哨兵机制[2] 信息同步与TILT模式

    Redis源码解析(1) 动态字符串与链表 Redis源码解析(2) 字典与迭代器 Redis源码解析(3) 跳跃表 Redis源码解析(4) 整数集合 Redis源码解析(5) 压缩列表 Redis ...

  2. 10年大厂程序员是如何高效学习使用redis的丨redis源码分析丨redis存储原理

    10年大厂程序员是怎么学习使用redis的 1. redis存储原理分析 2. redis源码学习分享 3. redis跳表和B+树详细对比分析 视频讲解如下,点击观看: 10年大厂程序员是如何高效学 ...

  3. 阅读 redis 源码,学习缓存淘汰算法 W-TinyLFU

    所有 IT 从业者都接触过缓存,一定了解基本工作原理,业界流行一句话: 缓存就是万金油,哪里有问题哪里抹一下 .那他的本质是什么呢? 上图代表从 cpu 到底层硬盘不同层次,不同模块的运行速度,上层多 ...

  4. redis源码学习笔记目录

    Redis源码分析(零)学习路径笔记 Redis源码分析(一)redis.c //redis-server.c Redis源码分析(二)redis-cli.c Redis源码剖析(三)--基础数据结构 ...

  5. Redis源码学习(20),学习感悟

      最近学习Redis源码也有半个月的时间了,有不少收获也有不少感悟,今天来好好聊聊我学习的感悟. 1 发现问题   人非圣贤孰能无过,只要是人难免会犯错,回顾我之前的学习历程,其实是可以发现不少的问 ...

  6. 为什么C/C++程序员都要阅读Redis源码之:Redis学习事件驱动设计

    0. 为什么我说C/C++程序员都要阅读Redis源码 主要原因就是『简洁』.如果你用源码编译过Redis,你会发现十分轻快,一步到位.其他语言的开发者可能不会了解这种痛,作为C/C++程序员,如果你 ...

  7. c++ 多线程 类成员函数_为什么我说C/C++程序员都要阅读Redis源码之:通过Redis学习事件驱动设计

    0. 为什么我说C/C++程序员都要阅读Redis源码 主要原因就是『简洁』.如果你用源码编译过Redis,你会发现十分轻快,一步到位.其他语言的开发者可能不会了解这种痛,作为C/C++程序员,如果你 ...

  8. 【Redis学习笔记】2018-05-30 Redis源码学习之Ziplist、Server

    作者:施洪宝 顺风车运营研发团队 一. 压缩列表 压缩列表是Redis的关键数据结构之一.目前已经有大量的相关资料,下面几个链接都已经对Ziplist进行了详细的介绍. http://origin.r ...

  9. matlab exm,exm 《Experiments with MATLAB》这本书的程序源码,附中文注释,简单易懂,是学习 238万源代码下载- www.pudn.com...

    文件名称: exm下载 收藏√  [ 5  4  3  2  1 ] 开发工具: matlab 文件大小: 1154 KB 上传时间: 2014-11-21 下载次数: 6 提 供 者: 刘晏池 详细 ...

  10. Tensorflow 2.x(keras)源码详解之第十五章:迁移学习与微调

      大家好,我是爱编程的喵喵.双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中.从事机器学习以及相关的前后端开发工作.曾在阿里云.科大讯飞.CCF等比赛获得多次Top名次.现 ...

最新文章

  1. Javascript中的apply、call、bind
  2. RabbitMq--1
  3. 数字后端基本概念介绍——Row
  4. 【Java方法】统计数组内不同元素个数及每个元素的数量
  5. linux添加动态连接库,CentOS下如何添加动态链接库?
  6. 网络驱动器映射成功但无法更新文件_FTP映射网络驱动器-ExpanDrive for mac
  7. 骨干考核系统系统流程及整体规则
  8. 电源模块-LM5117-BUCK- 电路
  9. pl/sql编程语言
  10. 心理学第二周学习笔记:心理学的历史和流派
  11. 2023厦门大学计算机考研信息汇总
  12. 《卓有成效的管理者(The Effective Executive)》读后感
  13. 从零开始写高性能的人脸识别服务器(一)
  14. SSL证书怎么部署,SSL证书需要怎么安装你知道吗?
  15. layui通用后台模板
  16. 解决报错Connection terminated as request was larger than 10485760
  17. Matplotlib 画图如何取消图边框
  18. CNPC海外操作人员英语日常用语900句
  19. 哪种耳机适合跑步用、跑步运动耳机推荐
  20. python海龟库写名字

热门文章

  1. 关于python中的复数类型、下列说法错误的是_关于 Python中的复数,下列说法错误的是( )_学小易找答案...
  2. 错误:php70w-common conflicts with php-common-5.3.3-49.el6.x86_64 You could try using --skip-broken to
  3. 什么是虚拟化技术?虚拟化常见架构
  4. 大咖 | 香港中文大学汤晓鸥教授:人工智能让天下没有难吹的牛!
  5. opencv 图像色彩空间与应用转换
  6. 软银与Coinbase投资的项目Tribal即将上线Coinlist
  7. 计算机操作系统(汤小丹、梁红兵)第四版课后习题答案(七)
  8. SEAL全同态加密开源库(七) rns剩余数系统-源码解析
  9. Ubuntu使用日志2(在Eclipse中搭建C++交叉编译环境)
  10. 第一次随笔之寒假作业一