前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Redis hash类型

Redis hash类型

作者头像
邹志全
发布2019-07-31 11:15:53
7650
发布2019-07-31 11:15:53
举报
文章被收录于专栏:EffectiveCodingEffectiveCoding

Hash 表示的是一种字段与值之间的映射关系,与很多编程语言中的map或者字典类型类似。Redis其实本身就可以本身就可以看作一个大Hash,其字符串类型的键关联到字符串或者链表之类的数据对象。而Redis 中的数据对象也可以再次使用Hash,其字段和值必须是字符串类型,在这里其实可以简单的理解为一个大Map。

先看一下使用:HMSET(设置多个属性值,如果存在会产生覆盖)、HSET(设置一个属性值,如果存在会产生覆盖)、HMGET(从一个Hash中获取多个属性值)、HGET(从一个Hash中获取一个属性值)、HEXISTS(判断Hash中某个属性是否存在)、HGETALL(获取一个Hash中的所有属性和值)、HSCAN(增量获取属性和值)从使用上来说,Hash看起来跟list是类似的,都是先初始化一个Hash,然后进行对应的操作,并且在Hash为空是Redis也会帮我们直接删除。

一个Hash最多能容纳8388607(2^23 - 1)个字段,如果这个量打满或者说量相对较大的时候,一个HGETALL命令会直接直接夯住Redis 服务器(Redis 之前提过执行其他命令时是会阻塞其他命令的),所以数据量非常大的时候Redis会一直在执行HGETALL 从而导致其他命令没法顺利执行。如果存在这种场景,尽可能的使用HSCAN来完成操作。

下面来看看关于Hash在Redis中的具体实现:

Redis Hash 类型底层有两种编码格式:ziplist、hashtable,就默认来说Hash对象保存的所有键值对的字符串都小于64字符,并且Hash对象保存的键值对数量小于512时是使用ziplist的,如果无法同时满足这两个则使用hashtable编码,这里是可以由使用者自定义进行控制的,redis提供了这么几个参数:

hash-max-ziplist-value 64 // ziplist中最大能存放的值长度

hash-max-ziplist-entries 512 // ziplist中最多能存放的

这里采用两个结构还是因为空间和时间之间的衡量,如果量还好的话直接使用ziplist 进行存储,时间上还ok 但空间上非常小,但如果是Hashtable 时间上ok,但空间不太合适,在量小并且长度较小的时候ziplist是比较占优势的,通常来说大多数场景都是符合那两个要求的。具体关于ziplist、zipmap等后续会有单独的文章进行描述。

下面来看Hash对象的底层实现: src/t_hash.c

按照命令使用顺序,先来看HSET:

先来看注释内容:

image.png

使用上:

1、添加新字段,如果旧字段已经存在,则用新值覆盖旧字段。

2、在插入时返回0,在更新时返回1。

源码解释:

默认情况下,将复制键和值SDS字符串,因此调用方保留所传递的字符串的所有权。然而,可以通过传递适当的标志(可能按位或按位)来影响这种行为:

HASH_SET_TAKE_FIELD——SDS 参数的所有权传递给函数。

HASH_SET_TAKE_VALUE——SDS值所有权传递给函数。

HASH_SET_COPY对应于不传递任何标志,默认需要时复制值。

接下来是源码:

首先判断编码:

如果是ziplist编码,先得到ziplist的head,然后检查fptr是否已经存在,如果存在的话取ziplist的next(也就是对应的value),之后标示更新操作,先删除旧的值,然后插入新的值。如果不存在的话,也就代表着不是一个更新操作,则直接把对应的键值对插入到ziplist的尾部,先插入key,再插入value。

如果是hashtable编码的话,在dict中查找对应的field,如果存在的话执行更新操作,如果不存在dictAdd 添加新的键值对就好了

关于宏定义的描述就跟上面注释说的一样:

flag为HASH_SET_COPY,field与value没有释放掉空间

flag为HASH_SET_TAKE_VALUE则value的值会释放掉

flag为HASH_SET_TAKE_FIELD则field的值会释放掉

image.png

上面提到了关于Hash 底层两种ziplist的转换(实际上 是ziplist向hashtable转换,单向不可逆),下面就来看一下插的节点过多或者长度过长之后带来的转换操作

因为在函数中用到了t_hash 中所定义的迭代器,所以先来看一下迭代器的实现:src/t_hash L:324

首先完成内存空间的分配,然后根据不同的编码设置不同的成员,最后返回一个字典迭代器给di成员就完成了初始化步骤。

image.png

具体迭代操作繁盛在hashTypeNext函数中,先声明了这一堆指针,如果当前迭代器为空,则初始化只想ziplist的第一个节点,如果不为空则只想下一个key节点,fptr的下个节点就是对应的值节点,然后更新参数,如果是OBJ_ENCODING_HT编码,直接调用哈希迭代器就可以了。

image.png

具体转化函数:src/t_hash.c L:465

第一行是类型的转化,根据enc来确定,一般来说是从ziplist转化为dict。

如果enc是ziplist则不进行转换,如果是ht的话,先初始化迭代器,创建新的表,不断的插入下一个(前面提到的迭代器操作),最后释放迭代器,然后完成转换。

image.png

然后是对应的删除函数和长度函数:

首先是删除函数 同样的判断是哪种编码,然后找到对应的节点删除就好了,如果是ht编码的话,在删除之后通常需要一个resize操作。

长度的话,如果是ziplist直接 /2(因为有一半是值节点),如果是ht,看一下字典熟练就好了。

image.png

整个t_hash 的使用及实现就先说这么多。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019.07.18 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 Redis
腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档