Redis源码学习(10),t_hash.c 学习(一),hset、hmset 命令学习
学习完 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这个方法,从头到尾介绍了下里面出现的方法,这样就能串联在一起知道这个方法整体的细节,先来看下整体的流程。
- 获取键对象,如果不存在就创建一个。
- 根据键和值的数据长度来判断数据结构是否要转换。
- 判断键和值两个参数是否要进行编码动作。
- 调用 hashTypeSet 这个方法来设置键值对,并返回更新状态。
- 响应更新新增状态结果 。
- 标记键被修改。
- 变更状态递增。
通过源代码我们可以知道,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 代码理解
- 校验参数数量是否为偶数。
- 获取hash键对象。
- 根据所有键和值,判断是否进行数据结构转换。
- 遍历所有键和值,调用 hashTypeSet 进行键值对写入。
- 响应ok。
- 标记键被修改,触发通知事件。
3 总结
- Redis中hash 是两种数据结构实现的,一种是压缩表,一种是哈希表。
- hset、hmset命令会判断每次设置的值是否会引起数据结构转换。
- hset 在设置键值对的时候,如果没有会先新建一个键值对。
- hset 新建和更新的时候返回值不同。
Redis源码学习(10),t_hash.c 学习(一),hset、hmset 命令学习相关推荐
- Redis源码解析(15) 哨兵机制[2] 信息同步与TILT模式
Redis源码解析(1) 动态字符串与链表 Redis源码解析(2) 字典与迭代器 Redis源码解析(3) 跳跃表 Redis源码解析(4) 整数集合 Redis源码解析(5) 压缩列表 Redis ...
- 10年大厂程序员是如何高效学习使用redis的丨redis源码分析丨redis存储原理
10年大厂程序员是怎么学习使用redis的 1. redis存储原理分析 2. redis源码学习分享 3. redis跳表和B+树详细对比分析 视频讲解如下,点击观看: 10年大厂程序员是如何高效学 ...
- 阅读 redis 源码,学习缓存淘汰算法 W-TinyLFU
所有 IT 从业者都接触过缓存,一定了解基本工作原理,业界流行一句话: 缓存就是万金油,哪里有问题哪里抹一下 .那他的本质是什么呢? 上图代表从 cpu 到底层硬盘不同层次,不同模块的运行速度,上层多 ...
- redis源码学习笔记目录
Redis源码分析(零)学习路径笔记 Redis源码分析(一)redis.c //redis-server.c Redis源码分析(二)redis-cli.c Redis源码剖析(三)--基础数据结构 ...
- Redis源码学习(20),学习感悟
最近学习Redis源码也有半个月的时间了,有不少收获也有不少感悟,今天来好好聊聊我学习的感悟. 1 发现问题 人非圣贤孰能无过,只要是人难免会犯错,回顾我之前的学习历程,其实是可以发现不少的问 ...
- 为什么C/C++程序员都要阅读Redis源码之:Redis学习事件驱动设计
0. 为什么我说C/C++程序员都要阅读Redis源码 主要原因就是『简洁』.如果你用源码编译过Redis,你会发现十分轻快,一步到位.其他语言的开发者可能不会了解这种痛,作为C/C++程序员,如果你 ...
- c++ 多线程 类成员函数_为什么我说C/C++程序员都要阅读Redis源码之:通过Redis学习事件驱动设计
0. 为什么我说C/C++程序员都要阅读Redis源码 主要原因就是『简洁』.如果你用源码编译过Redis,你会发现十分轻快,一步到位.其他语言的开发者可能不会了解这种痛,作为C/C++程序员,如果你 ...
- 【Redis学习笔记】2018-05-30 Redis源码学习之Ziplist、Server
作者:施洪宝 顺风车运营研发团队 一. 压缩列表 压缩列表是Redis的关键数据结构之一.目前已经有大量的相关资料,下面几个链接都已经对Ziplist进行了详细的介绍. http://origin.r ...
- matlab exm,exm 《Experiments with MATLAB》这本书的程序源码,附中文注释,简单易懂,是学习 238万源代码下载- www.pudn.com...
文件名称: exm下载 收藏√ [ 5 4 3 2 1 ] 开发工具: matlab 文件大小: 1154 KB 上传时间: 2014-11-21 下载次数: 6 提 供 者: 刘晏池 详细 ...
- Tensorflow 2.x(keras)源码详解之第十五章:迁移学习与微调
大家好,我是爱编程的喵喵.双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中.从事机器学习以及相关的前后端开发工作.曾在阿里云.科大讯飞.CCF等比赛获得多次Top名次.现 ...
最新文章
- Javascript中的apply、call、bind
- RabbitMq--1
- 数字后端基本概念介绍——Row
- 【Java方法】统计数组内不同元素个数及每个元素的数量
- linux添加动态连接库,CentOS下如何添加动态链接库?
- 网络驱动器映射成功但无法更新文件_FTP映射网络驱动器-ExpanDrive for mac
- 骨干考核系统系统流程及整体规则
- 电源模块-LM5117-BUCK- 电路
- pl/sql编程语言
- 心理学第二周学习笔记:心理学的历史和流派
- 2023厦门大学计算机考研信息汇总
- 《卓有成效的管理者(The Effective Executive)》读后感
- 从零开始写高性能的人脸识别服务器(一)
- SSL证书怎么部署,SSL证书需要怎么安装你知道吗?
- layui通用后台模板
- 解决报错Connection terminated as request was larger than 10485760
- Matplotlib 画图如何取消图边框
- CNPC海外操作人员英语日常用语900句
- 哪种耳机适合跑步用、跑步运动耳机推荐
- python海龟库写名字
热门文章
- 关于python中的复数类型、下列说法错误的是_关于 Python中的复数,下列说法错误的是( )_学小易找答案...
- 错误:php70w-common conflicts with php-common-5.3.3-49.el6.x86_64 You could try using --skip-broken to
- 什么是虚拟化技术?虚拟化常见架构
- 大咖 | 香港中文大学汤晓鸥教授:人工智能让天下没有难吹的牛!
- opencv 图像色彩空间与应用转换
- 软银与Coinbase投资的项目Tribal即将上线Coinlist
- 计算机操作系统(汤小丹、梁红兵)第四版课后习题答案(七)
- SEAL全同态加密开源库(七) rns剩余数系统-源码解析
- Ubuntu使用日志2(在Eclipse中搭建C++交叉编译环境)
- 第一次随笔之寒假作业一