Redis数据结构和编码

160 阅读8分钟

1、Redis对key的简单操作

1、查看所有的键 keys *

2、查看键的总数dbsize

3、查看键是否存在exists key

4、删除键del key [key ...]

5、过期键expire key seconds (Redis支持对键添加过期时间,当超过过期时间后,会自动删除键)

6、查看键的过期时间ttl key (ttl命令会返回键的剩余过期时间,它有3种返回值)

  • 大于等于0的整数:键剩余的过期时间。
  • -1:键没设置过期时间。
  • -2:键不存在 。

7、查看键的数据结构 type key (如果键不存在,则返回none)

8、查看键的编码格式object encoding key

2、数据结构、内部编码

type命令返回的就是当前键的数据结构类型,Redis一共有五种数据结构类型:string(字符串)、hash(哈希)、list(列表)、set(集合)、zset(有序集合)。每种数据结构都由自己的内部编码实现,而且是多种实现方式,Redis会在合适的场合选择合适的内部编码。

image-20220329120012430

Redis这样设计有两个好处:

第一,可以改进内部编码,而对外的数据结构和命令没有影响,这样一旦开发出更优秀的内部编码,无需改动外部数据结构和命令。Redis3.2提供了quicklist,结合了ziplist和linkedlist两者的优势,为列表类型提供了一种更为优秀的内部编码实现,而对外部用户来说基本感知不到。

第二,多种内部编码实现可以在不同场景下发挥各自的优势,例如ziplist比较节省内存,但是在列表元素比较多的情况下,性能会有所下降,这时候Redis会根据配置选项将列表类型的内部实现转换为 linkedlist。

1、字符串

字符串类型是Redis最基础的数据结构。字符串类型的值实际可以是字符串(简单的字符串、复杂的字符串(例如JSON、XML))、数字(整数、浮点数),甚至是二进制(图片、音频、视频),但是值最大不能超过512MB。

字符串类型的内部编码有3种:

  • int:8个字节的长整型。
  • embstr:小于等于39个字节的字符串。
  • raw:大于39个字节的字符串。

Redis会根据当前值的类型和长度决定使用哪种内部编码实现。

 # int编码类型
 127.0.0.1:6379> set key1 1234
 OK
 127.0.0.1:6379> object encoding key1
 "int"
 ​
 # embstr和raw编码类型
 127.0.0.1:6379> set str1 qwerasdf
 OK
 127.0.0.1:6379> set str2 qqqqqqqqqqqqwwwwwwwwwwwweeeeeeeeeeeeerrrrrrrrrrttttttttttttyyyyyyyyyyyyyuuuuuuuuuuuuuiiiiiiiiii
 OK
 ​
 127.0.0.1:6379> object encoding str1
 "embstr"
 127.0.0.1:6379> object encoding str2
 "raw"

主要功能:缓存、计数、共享Session。

2、哈希

几乎所有的编程语言都提供了哈希(hash)类型,它们的叫法可能是哈希、字典、关联数组。在Redis中,哈希类型是指键值本身又是一个键值对结构,形如value={{field1,value1},...{fieldN,valueN}},Redis键值对和哈希类型二者的关系可以用下图表示。

image-20220329104515879

 # 设置hash
 # > hset key field value
 127.0.0.1:6379> hset id:123 name fong age 18
 (integer) 2
 ​
 # 获取值(如果键或field不存在,会返回nil)
 # > hget key field
 127.0.0.1:6379> HGET id:123 name
 "fong"
 127.0.0.1:6379> HGET id:123 name1
 (nil)
 ​
 # 删除field
 # > hdel key field [field ...]
 127.0.0.1:6379> HDEL id:123 name age
 (integer) 2
 ​
 # 计算field个数
 # > hlen key
 127.0.0.1:6379> hlen id:123
 (integer) 0
 ​
 # 批量设置或获取field-value
 # > hmget key field [field ...] 
 # > hmset key field value [field value ...]
 ​
 # 获取所有field
 # > hkeys key
 127.0.0.1:6379> hkeys id:123
 1) "name"
 2) "age"
 ​
 # 获取所有value
 # > hvals key
 127.0.0.1:6379> hvals id:123
 1) "fong"
 2) "18"

哈希类型中的映射关系叫作field-value,注意这里的value是指field对应的值,不是键对应的值,请注意value在不同上下文的作用。

哈希类型的内部编码有两种:

ziplist(压缩列表):当哈希类型元素个数小于hash-max-ziplist-entries配置(默认512个)、同时所有值都小于hash-max-ziplist-value配置(默认64字节)时,Redis会使用ziplist作为哈希的内部实现,ziplist使用更加紧凑的结构实现多个元素的连续存储,所以在节省内存方面比hashtable更加优秀。

hashtable(哈希表):当哈希类型无法满足ziplist的条件时,Redis会使用hashtable作为哈希的内部实现,因为此时ziplist的读写效率会下降,而hashtable的读写时间复杂度为O(1)。

当field个数比较少且没有大的value时,内部编码为ziplist。当有value大于64字节或者field个数超过512,内部编码会由ziplist变为hashtable。

使用场景:

image-20220329113736759

相比于使用字符串序列化缓存用户信息,哈希类型变得更加直观,并且在更新操作上会更加便捷。可以将每个用户的id定义为键后缀,多对field-value对应每个用户的属性。

但是需要注意的是哈希类型和关系型数据库有两点不同之处:

  • 哈希类型是稀疏的,而关系型数据库是完全结构化的,例如哈希类型每个键可以有不同的field,而关系型数据库一旦添加新的列,所有行都要为其设置值(即使为NULL)。
  • 关系型数据库可以做复杂的关系查询,而Redis去模拟关系型复杂查询开发困难,维护成本高。

3、列表

列表(list)类型是用来存储多个有序的字符串,列表中的每个字符串称为元素(element),一个列表最多可以存储2的32次方减1个元素。在Redis中,可以对列表两端插入(push)和弹出(pop),还可以获取指定范围的元素列表、获取指定索引下标的元素等。列表是一种比较灵活的数据结构,它可以充当栈和队列的角色,在实际开发上有很多应用场景。

列表基础操作:

image-20220329134455132

image-20220329134326272

命令:

 # 从右边插入元素
 # > rpush key value [value ...]
 ​
 # 从左边插入元素
 # > lpush key value [value ...]
 ​
 # 向某个元素前或者后插入元素
 # > linsert key before|after pivot value
 ​
 # 获取指定范围内的元素列表
 # > lrange key start end
 ​
 # 获取列表指定索引下标的元素
 # > lindex key index
 ​
 # 获取列表长度
 # > llen key
 ​
 # 从列表左侧弹出元素
 # > lpop key
 ​
 # 删除指定元素
 # > lrem key count value
 # lrem命令会从列表中找到等于value的元素进行删除,根据count的不同 分为三种情况:
 ## count>0,从左到右,删除最多count个元素。
 ## count<0,从右到左,删除最多count绝对值个元素。
 ## count=0,删除所有。

列表类型有两个特点:

  • 列表中的元素是有序的。
  • 列表中的元素可以是重复的。

列表类型的内部编码有两种:

  • ziplist(压缩列表):当列表的元素个数小于list-max-ziplist-entries配置(默认512个),同时列表中每个元素的值都小于list-max-ziplist-value配置时(默认64字节),Redis会选用ziplist来作为列表的内部实现来减少内存的使用。
  • linkedlist(链表):当列表类型无法满足ziplist的条件时,Redis会使用linkedlist作为列表的内部实现。

Redis3.2版本提供了quicklist内部编码,简单地说它是以一个ziplist为节点的linkedlist,它结合了ziplist和linkedlist两者的优势,为列表类型提供了一种更为优秀的内部编码实现。

使用场景 :消息队列、文章列表

4、集合

集合(set)类型也是用来保存多个的字符串元素,但和列表类型不一样的是,集合中不允许有重复元素,并且集合中的元素是无序的,不能通过索引下标获取元素。

命令:

 # 添加元素
 # > sadd key element [element ...]
 ​
 # 删除元素
 # > srem key element [element ...]
 ​
 # 计算元素个数
 # > scard key
 ​
 # 判断元素是否在集合中
 # > sismember key element
 ​
 # 获取所有元素
 # > smembers key

集合间操作 :

 # 集合的交集
 # > sinter key [key ...]
 ​
 # 集合的并集
 # > suinon key [key ...]
 ​
 # 集合的差集
 # > sdiff key [key ...]
 ​
 # 将交集、并集、差集的结果保存
 # > sinterstore destination key [key ...] 
 # > suionstore destination key [key ...] 
 # > sdiffstore destination key [key ...]

集合类型的内部编码有两种:

  • intset(整数集合):当集合中的元素都是整数且元素个数小于set-max-intset-entries配置(默认512个)时,Redis会选用intset来作为集合的内部实现,从而减少内存的使用。
  • hashtable(哈希表):当集合类型无法满足intset的条件时,Redis会使用hashtable作为集合的内部实现。

当元素个数较少且都为整数时,内部编码为intset,当元素个数超过512个或者某个元素不为整数时,内部编码变为hashtable。

使用场景:标签、随机数抽奖等

5、有序集合

有序集合相对于哈希、列表、集合来说会有一点点陌生,但既然叫有序集合,那么它和集合必然有着联系,它保留了集合不能有重复成员的特性,但不同的是,有序集合中的元素可以排序。但是它和列表使用索引下标作为排序依据不同的是,它给每个元素设置一个分数(score)作为排序的依据。(有序集合中的元素不能重复,但是score可以重复)

image-20220329140759295

命令:

 # 添加成员
 # > zadd key score member [score member ...]
 ​
 # 计算成员个数
 # > zcard key
 ​
 # 计算某个成员的分数
 # > zscore key member
 ​
 # 计算成员的排名
 # > zrank key member
 # > zrevrank key member
 ​
 # 删除成员
 # > zrem key member [member ...]
 ​
 # 增加成员的分数
 # > zincrby key increment member
 ​
 # 返回指定排名范围的成员
 # > zrange key start end [withscores]
 # > zrevrange key start end [withscores]
 ​
 # 返回指定分数范围的成员
 # > zrangebyscore key min max [withscores] [limit offset count]
 # > zrevrangebyscore key max min [withscores] [limit offset count]
 ​
 # 返回指定分数范围成员个数
 # > zcount key min max
 ​
 # 删除指定排名内的升序元素
 # > zremrangebyrank key start end
 ​
 # 删除指定分数范围的成员
 # > zremrangebyscore key min max

有序集合类型的内部编码有两种:

ziplist(压缩列表):当有序集合的元素个数小于zset-max-ziplist-entries配置(默认128个),同时每个元素的值都小于zset-max-ziplist-value配置(默认64字节)时,Redis会用ziplist来作为有序集合的内部实现,ziplist可以有效减少内存的使用。

skiplist(跳跃表):当ziplist条件不满足时,有序集合会使用skiplist作为内部实现,因为此时ziplist的读写效率会下降。

当元素个数较少且每个元素较小时,内部编码为skiplist,当元素个数超过128个,内部编码变为ziplist,当某个元素大于64字节时,内部编码也会变为hashtable。

应用场景:排行榜、点赞等