都别搞错了,最正确的回答就是Redis有7种数据类型

Redis/缓存系统
300
0
0
2022-03-27
标签   Redis

前言

面试官:Redis 有哪几种数据类型?存储原理是什么?具体适应哪些应用场景?是否历历在目,这是 Redis 关于数据类型的面试 3 连问,是除“Redis 持续化”外的最常见 Redis 考题。

查谷歌众说纷纭

说法一:5 种

Redis 支持 5 种数据类型:

  • String(字符串)
  • List(列表)
  • Set(集合)
  • Sorted Set(有序集合)
  • Hash(哈希)

这也是被行业普遍认可,最最常见的答案。至于这 5 种类型的详解,网上已经铺天盖地,这里不打算重复探讨,请读者自行温习。

都别搞错了,最正确的回答就是Redis有7种数据类型

说法二:6 种

包含了“说法一”的 5 种,还包含了:HyperLogLog(基数)。

也就是:String(字符串)、List(列表)、Set(集合)、Sorted Set(有序集合)、Hash(哈希)、HyperLogLog(基数)共 6 种。

都别搞错了,最正确的回答就是Redis有7种数据类型

说法三:9 种

包含了“说法二”的 6 种,还包含了:Bitmap(位集合)、Geospatial(地理空间索引)、Streams(流信息)3 种。

也就是:String(字符串)、List(列表)、Set(集合)、Sorted Set(有序集合)、Hash(哈希)、HyperLogLog(基数)、Bitmap(位集合)、Geospatial(地理空间索引)、Streams(流信息)共 9 种。

还有一说,并未包含 Streams(流信息),但是包含了 BloomFilter(布隆过滤器),这个不重要,但都称是 9 种,尚未见有 10 种的说法。

都别搞错了,最正确的回答就是Redis有7种数据类型

从官网找答案

英文官网:https://redis.io/,中文官网:http://www.redis.cn/,首页如下:

都别搞错了,最正确的回答就是Redis有7种数据类型

请留意这一句:

Redis provides data structures such as strings, hashes, lists, sets, sorted sets with range queries, bitmaps, hyperloglogs, geospatial indexes, and streams.

很明显,官网提到 Redis 支持的数据类型一共有 9 种。跟上文的“说法三”基本一致。

另外值得注意的是,中文官网没有提及 Stream,也就是漏了一句话。因为 Stream 是在 2018 年 10 月 5.0 版本引入,但是中文官网至今没有更新,是个非常明显的文案 Bug(不知道反馈被采纳会不会有奖金)。

那么问题到此解决了?还没有!问题才刚刚开始。

具体问题具体分析

“说法一:5 种” 为什么会被行业普遍认可

先来看看 Redis 的各种高级功能类型被引入的版本,如下表:

都别搞错了,最正确的回答就是Redis有7种数据类型

很明显,原因是:这些功能都是后续版本陆续引入的,5种数据类型乃最经典的 5 种类型,所以代代相传,传承已久。

再来看看 Redis的各个大版本的发布时间,如下表:

都别搞错了,最正确的回答就是Redis有7种数据类型

也就是说,“5 种数据类型”的认知,业界持续已有 10 年之久,认知的错误也有 10 年之久。

“说法三:9 种” 是否正确

要回答这个问题,先了解 Redis 的数据类型如何查看,可通过 type KEY_NAME 命令。

另外,通过 object encoding KEY_NAME 命令可查具体的编码结构,这里仅稍作提及,不在本文的讨论范围内。

①String

localhost:6379> set str:hello world 
OK 
localhost:6379> get str:hello 
"world" 
localhost:6379> type str:hello 
string 

②Bitmap

localhost:6379> setbit str:a 1 1 
(integer) 0 
localhost:6379> setbit str:a 2 1 
(integer) 0 
localhost:6379> setbit str:a 7 1 
(integer) 0 
localhost:6379> get str:a 
"a" 
localhost:6379> type str:a 
string 

很明显,Bitmap 底层也是 String 实现,赋值的每一个 bit 均对应 ASCII 码的二进制位。

③HyperLogLog

127.0.0.1:6379> PFADD hyperLogLog:db "redis" 
(integer) 1 
127.0.0.1:6379> PFADD hyperLogLog:db "mongodb" 
(integer) 1 
127.0.0.1:6379> PFADD hyperLogLog:db "mysql" 
(integer) 1 
127.0.0.1:6379> PFCOUNT hyperLogLog:db 
(integer) 3 
127.0.0.1:6379> get hyperLogLog:db 
"HYLL\x01\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00H\x91\x80\\g\x84[\x03" 
127.0.0.1:6379> type hyperLogLog:db 
string 

很明显,HyperLogLog 底层也是 String 实现,与其说 HyperLogLog 是一种单独的数据类型,倒不如说是对 String 数据类型做 API 封装的应用程序。

④归纳

其他几种高级功能类型的验证方式同,这里不做赘述,留给读者自行验证。

这里归纳结论如下:

都别搞错了,最正确的回答就是Redis有7种数据类型

绕了一圈似乎又回到了起点,“说法一:5 种”其实并没有错?毕竟任何类型的底层都是基于 5 种之一实现的。接着往下说。

Talk is cheap, Show me the code.

能说算不上什么,有本事就把你的代码给我看看。

都别搞错了,最正确的回答就是Redis有7种数据类型

源码文件列表

如下图:

都别搞错了,最正确的回答就是Redis有7种数据类型

t_(type) 开头的,有且仅有 6 个,除了“5 种数据类型”外,还包含了:t_stream。

Stream 是 Redis 5.0 版本引入的一个新的数据类型,支持消费者组,借鉴 Kafka 设计的支持多播的可持久化消息队列(支持 group,不支持 partition)。

我们做下验证:

localhost:6379[2]> XADD stream:info * name aku alias bumblebee age 35 address sz 
"1615012000623-0" 
localhost:6379[2]> type stream:info 
stream 

没有问题:6 种,让我们重新梳理一下:

  • String(字符串)
  • List(列表)
  • Set(集合)
  • Sorted Set(有序集合)
  • Hash(哈希)
  • Streams(流信息)

源码就是源码,让人豁然开朗,查谷歌众说纷纭、千篇一律,确实都不对!

那么问题答案到此解决?还没有。但现在已经不是刚刚开始了,只差最后一步。

源码内容

不能徒有其表,只看源码文件列表,不看源码内容。

都别搞错了,最正确的回答就是Redis有7种数据类型

这是关于类型的枚举定义,0 到 6,什么?OBJ_MODULE?这是什么鬼?

请留意这一句描述:

* The "module" object type is a special one that signals that the object

* is one directly managed by a Redis module.

尤其是 special 一词,这是 special 的类型,其余 6 类都是非 special 类型。既然 special,为什么其枚举值是 5 会夹在 hash 和 stream 的非 special 之间?

历史原因,Redis 4.0 引入了模块扩展功能,当时已经认为是最后一个类型。

但是 Redis 5.0 又引入了 Stream 数据结构,可能是觊觎 Kafka 的市场份额,说白跟 RocketMQ 一样都是仿照 Kafka 去实现的。

言归正传,所以该枚举值的定义是不是跟我们日常业务开发的场景似曾相识,因为状态值编号已经被占了,那么新加的状态值就只能往后面排,导致五花八门一点都不连续。没错,就这么接地气。

那么,module 用在什么场景?有很多场景,举个最常用的例子:Leaky Bucket(漏桶算法),也就是 Redis 4.0 引入的 redis-cell 模块。

示例如下:

> cl.throttle module:leaky 14 30 60 1 
1) (integer) 0          # 0 表示允许  1 表示拒绝 
2) (integer) 15         # 漏斗容量 capacity 
3) (integer) 14         # 漏斗剩余空间 left_quota 
4) (integer) -1         # 如果拒绝了,需要多长时间后再重试,单位秒 
5) (integer) 2          # 多长时间后,漏斗完全空出来,单位秒 

那么问题答案到此解决?是的,通过分析源码终于有了结论。

结论

Q:Redis 有哪几种数据类型?

A:Redis 6.0 最新版本有且仅有 7 种。

按源码中枚举值定义的顺序,分别为:

  • String(字符串)
  • List(列表)
  • Set(集合)
  • Sorted Set(有序集合)
  • Hash(哈希)
  • Module(模块)
  • Streams(流信息)

Q:高级功能类型,比如 HyperLogLog、Bitmap 等呢?

A:高级功能类型是对数据类型做 API 封装的应用程序。

HyperLogLog、Bitmap、Bloom Filter 的底层都是 String 数据类型,Geospatial 的底层是 Sorted Set 数据类型,cl.throttle(Redis-Cell) 的底层是 Module 数据类型。均可通过 type KEY_NAME 命令逐一核对。

所以,当面试官下次问你“Redis 数据类型的面试 3 连问”时候,可以好好地怼回去了,让面试官看到你的理解、你对底层逻辑的掌握比面试官本人更系统、更专业,给面试官带来些许的惊喜,相信面试效果会完全不一样。

万一惊喜变成了惊吓怎么办?也许该团队是个固执己见的守旧团队,面试官的考题可能也只是来自照本宣科的题库,那么你可以把你的简历慢慢合上,挥一挥衣袖不带走一片云彩。

最后,Talk is cheap,Show me the code。实践才是检验真理的唯一标准,共勉,请不要再错下去了。

来源:
https://stor.51cto.com/art/202103/649648.htm