阿里云服务器ECS    
弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新 [咨询更多]
阿里云存储OSS
简单易用、多重冗余、数据备份高可靠、多层次安全防护安全性更强、低成本 [咨询更多]
阿里云数据库RDS
稳定可靠、可弹性伸缩、更拥有容灾、备份、恢复、监控、迁移等方面的全套解决方案 [咨询更多]
阿里云安全产品
DDoS高防IP、web应用防火墙、安骑士、sll证书、态势感知众多阿里云安全产品热销中 [咨询更多]
阿里云折扣优惠    
云服务器ECS、数据库、负载均衡等产品新购、续费、升级联系客服获取更多专属折扣 [咨询更多]
Redis提供了5种数据类型
2020-7-23    点击量:

      一、前言#
    Redis提供了5种数据类型:String(字符串)、Hash(哈希)、List(列表)、Set(集合)、Zset(有序集合),理解每种数据类型的特点对于redis的开发和运维非常重要。

5种数据类型

   Redis中的hash是我们经常使用到的一种数据类型,根据使用方式的不同,可以应用到很多场景中。

    二、实现分析#
    由上述结构图可知,Hash类型有以下两种实现方式:
    1、ziplist编码的哈希对象使用压缩列表作为底层实现
    2、hashtable编码的哈希对象使用字典作为底层实现
    1.ziplist编码作为底层实现#
    ziplist编码的哈希对象使用压缩列表作为底层实现,每当有新的键值对要加入到哈希对象时,程序会先将保存了键的压缩列表节点推入到压缩列表表尾,然后再将保存了值的压缩列表节点推入到压缩列表表尾,因此:
    保存了同一键值对的两个节点总是紧挨在一起,保存键的节点在前,保存值的节点在后;
    先添加到哈希对象中的键值对会被放在压缩列表的表头方向,而后来添加到哈希对象中的键值对会被放在压缩列表的表尾方向。
    例如,我们执行以下HSET命令,那么服务器将创建一个列表对象作为profile键的值:
    redis>HSETprofilename"Tom"
    (integer)1
    redis>HSETprofileage25
    (integer)1
    redis>HSETprofilecareer"Programmer"
    (integer)1

    profile键的值对象使用的是ziplist编码,其中对象所使用的压缩列表结构如下图所示。

如下图所示

如下图所示

    2.hashtable编码作为底层实现#
    hashtable编码的哈希对象使用字典作为底层实现,哈希对象中的每个键值对都使用一个字典键值对来保存:
    字典的每个键都是一个字符串对象,对象中保存了键值对的键;
    字典的每个值都是一个字符串对象,对象中保存了键值对的值。

    例如,如果前面profile键创建的不是ziplist编码的哈希对象,而是hashtable编码的哈希对象,那么这个哈希对象结构如下图所示。

哈希对象结构如下图所示

    三、命令实现#
    因为哈希键的值为哈希对象,所以用于哈希键的所有命令都是针对哈希对象来构建的,下表列出了其中一部分哈希键命令,以及这些命令在不同编码的哈希对象下的实现方法。

命令ziplist 编码实现方法hashtable 编码的实现方法
HSET首先调用 ziplistPush 函数, 将键推入到压缩列表的表尾, 然后再次调用 ziplistPush 函数, 将值推入到压缩列表的表尾。调用 dictAdd 函数, 将新节点添加到字典里面。
HGET首先调用 ziplistFind 函数, 在压缩列表中查找指定键所对应的节点, 然后调用 ziplistNext 函数, 将指针移动到键节点旁边的值节点, 最后返回值节点。调用 dictFind 函数, 在字典中查找给定键, 然后调用dictGetVal 函数, 返回该键所对应的值。
HEXISTS调用 ziplistFind 函数, 在压缩列表中查找指定键所对应的节点, 如果找到的话说明键值对存在, 没找到的话就说明键值对不存在。调用 dictFind 函数, 在字典中查找给定键, 如果找到的话说明键值对存在, 没找到的话就说明键值对不存在。
HDEL调用 ziplistFind 函数, 在压缩列表中查找指定键所对应的节点, 然后将相应的键节点、 以及键节点旁边的值节点都删除掉。调用 dictDelete 函数, 将指定键所对应的键值对从字典中删除掉。
HLEN调用 ziplistLen 函数, 取得压缩列表包含节点的总数量, 将这个数量除以 2 , 得出的结果就是压缩列表保存的键值对的数量。调用 dictSize 函数, 返回字典包含的键值对数量, 这个数量就是哈希对象包含的键值对数量。
HGETALL遍历整个压缩列表, 用 ziplistGet 函数返回所有键和值(都是节点)。遍历整个字典, 用 dictGetKey 函数返回字典的键, 用dictGetVal 函数返回字典的值。
    四、编码转换#
    当哈希对象可以同时满足以下两个条件时,哈希对象使用ziplist编码:
    哈希对象保存的所有键值对的键和值的字符串长度都小于64字节;
    哈希对象保存的键值对数量小于512个;
    不能满足这两个条件的哈希对象需要使用hashtable编码。
    注意:这两个条件的上限值是可以修改的,具体请看配置文件中关于hash-max-ziplist-value选项和hash-max-ziplist-entries选项的说明。
    对于使用ziplist编码的列表对象来说,当使用ziplist编码所需的两个条件的任意一个不能被满足时,对象的编码转换操作就会被执行:原本保存在压缩列表里的所有键值对都会被转移并保存到字典里面,对象的编码也会从ziplist变为hashtable。
    以下代码展示了哈希对象编码转换的情况:
    1.键的长度太大引起编码转换#
    #哈希对象只包含一个键和值都不超过64个字节的键值对
    redis>HSETbookname"MasteringC++in21days"
    (integer)1
    redis>OBJECTENCODINGbook
    "ziplist"
    #向哈希对象添加一个新的键值对,键的长度为66字节
    redis>HSETbooklong_long_long_long_long_long_long_long_long_long_long_description"content"
    (integer)1
    #编码已改变
    redis>OBJECTENCODINGbook
    "hashtable"

    2.值的长度太大引起编码转换#
    #哈希对象只包含一个键和值都不超过64个字节的键值对
    redis>HSETblahgreeting"helloworld"
    (integer)1
    redis>OBJECTENCODINGblah
    "ziplist"
    #向哈希对象添加一个新的键值对,值的长度为68字节
    redis>HSETblahstory"manystring...manystring...manystring...manystring...many"
    (integer)1
    #编码已改变
    redis>OBJECTENCODINGblah
    "hashtable"

    3.键值对数量过多引起编码转换#
    #创建一个包含512个键值对的哈希对象
    redis>EVAL"fori=1,512doredis.call('HSET',KEYS[1],i,i)end"1"numbers"
    (nil)
    redis>HLENnumbers
    (integer)512
    redis>OBJECTENCODINGnumbers
    "ziplist"
    #再向哈希对象添加一个新的键值对,使得键值对的数量变成513个
    redis>HMSETnumbers"key""value"
    OK
    redis>HLENnumbers
    (integer)513
    #编码改变
    redis>OBJECTENCODINGnumbers
    "hashtable"

    总结1.Hash类型两种编码方式,ziplist与hashtable
    总结2.hashtable编码的哈希对象使用字典作为底层实现
    总结3.ziplist与hashtable编码方式之间存在转换

联系客服免费领取更多阿里云产品新购、续费升级折扣,叠加官网活动折上折更优惠