前言
面试官:Redis 有哪几种数据类型?存储原理是什么?具体适应哪些应用场景?是否历历在目,这是 Redis 关于数据类型的面试 3 连问,是除“Redis 持续化”外的最常见 Redis 考题。
查谷歌众说纷纭
说法一:5 种
Redis 支持 5 种数据类型:
- String(字符串)
- List(列表)
- Set(集合)
- Sorted Set(有序集合)
- Hash(哈希)
这也是被行业普遍认可,最最常见的答案。至于这 5 种类型的详解,网上已经铺天盖地,这里不打算重复探讨,请读者自行温习。
说法二:6 种
包含了“说法一”的 5 种,还包含了:HyperLogLog(基数)。
也就是:String(字符串)、List(列表)、Set(集合)、Sorted Set(有序集合)、Hash(哈希)、HyperLogLog(基数)共 6 种。
说法三:9 种
包含了“说法二”的 6 种,还包含了:Bitmap(位集合)、Geospatial(地理空间索引)、Streams(流信息)3 种。
也就是:String(字符串)、List(列表)、Set(集合)、Sorted Set(有序集合)、Hash(哈希)、HyperLogLog(基数)、Bitmap(位集合)、Geospatial(地理空间索引)、Streams(流信息)共 9 种。
还有一说,并未包含 Streams(流信息),但是包含了 BloomFilter(布隆过滤器),这个不重要,但都称是 9 种,尚未见有 10 种的说法。
从官网找答案
英文官网:https://redis.io/,中文官网:http://www.redis.cn/,首页如下:
请留意这一句:
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 的各种高级功能类型被引入的版本,如下表:
很明显,原因是:这些功能都是后续版本陆续引入的,5种数据类型乃最经典的 5 种类型,所以代代相传,传承已久。
再来看看 Redis的各个大版本的发布时间,如下表:
也就是说,“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 封装的应用程序。
④归纳
其他几种高级功能类型的验证方式同,这里不做赘述,留给读者自行验证。
这里归纳结论如下:
绕了一圈似乎又回到了起点,“说法一:5 种”其实并没有错?毕竟任何类型的底层都是基于 5 种之一实现的。接着往下说。
Talk is cheap, Show me the code.
能说算不上什么,有本事就把你的代码给我看看。
源码文件列表
如下图:
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(流信息)
源码就是源码,让人豁然开朗,查谷歌众说纷纭、千篇一律,确实都不对!
那么问题答案到此解决?还没有。但现在已经不是刚刚开始了,只差最后一步。
源码内容
不能徒有其表,只看源码文件列表,不看源码内容。
这是关于类型的枚举定义,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。实践才是检验真理的唯一标准,共勉,请不要再错下去了。
网友评论