只要是正经用过 Redis 的人,List、Hash、Set、ZSet 这四个基础类型基本都能聊上几句。但面试官一旦把 Stream、Geospatial、HyperLogLog、Bitmaps、Bitfields 这几个名字摆到台面上,很多人就开始露怯了——平时生产环境很少碰,看文档又总觉得隔了一层,真到用的时候完全不知道选哪个。
这篇文章就专门把这五种高级数据类型掰开揉碎讲清楚。我会从它们各自解决的痛点出发,把底层原理、命令细节、适用场景和实际坑点都过一遍,最后给出一套可以照抄的实操案例。不管你是在准备 Redis 面试,还是想在项目里把 Redis 用得更深一点,这篇都能帮你省下不少翻文档的时间。
1. 内容整体设计与思路拆解
1.1 这五种类型到底解决了什么问题
很多人学 Redis 高级类型容易陷入一个误区:把命令背得滚瓜烂熟,却不知道它们是为了什么场景而生的。我建议换个思路,先看痛点,再看解决方案。
先看基础类型解决不了的事。List 可以当队列用,但它是"单消费者"模型,消息被一个消费者取走就没了,想搞多个消费者分组处理、各自维护消费进度,List 完全做不到。Pub/Sub 解决了广播问题,但它不持久化,消费者一掉线就错过消息。这两个痛点就是 Stream 出现的理由。
再看统计场景。要统计一个页面的独立访客数(UV),最直接的办法是用 Set 把每个用户 ID 存进去,然后 SCARD。但用户量一旦到千万级,这个 Set 占用的内存就非常可观了。HyperLogLog 用固定 12KB 内存就能统计任意数量级的基数,误差控制在 0.81% 左右,这就是典型的"用精度换内存"。
还有一类场景和位置相关。比如外卖 App 要查"附近 3 公里有哪些门店",普通做法是把所有门店坐标拉到应用层,自己算距离再排序。Geospatial 类型直接在 Redis 里完成了距离计算和范围检索,一条命令搞定。它的底层其实是一个 ZSet,只不过 score 存的是经纬度经过特殊编码后的值。
Bitmaps 和 Bitfields 则瞄准了"极致省内存"的方向。一个用户只有两种状态(在线/离线、签到/未签到),用字符串存太浪费,位图一个用户只占 1 bit。Bitfields 更进一步,允许你把多个小整数紧凑地塞进同一个字符串里,适合做计数器、评分这类数据。
1.2 方案选型:什么时候该用,什么时候别硬上
理解了各自解决的问题,还有一个更实际的问题:项目里到底该怎么选?这里我给一个我自己的判断框架,不一定权威,但实战中挺好用。
Stream 适合轻量级消息队列场景。团队不想引入 Kafka 或 RabbitMQ,或者消息量本身不大(每秒几千条以内),用 Stream 完全够。但如果你需要消息堆积能力特别强、要精确的分区顺序保证、要对接 Hadoop 生态,那还是老实上 Kafka。Redis Stream 不是要取代专业 MQ,它更像是"Redis 顺手把消息队列的活也干了"。
Geospatial 适合所有基于 LBS 的"附近"检索需求,不管是门店、车辆还是好友。不过要注意,它在跨大洲这种超远距离计算时精度会下降,而且不适合做复杂的多边形区域判断。真要做行政区划级的地理围栏,还是得用专业的 GIS 方案。
HyperLogLog 只做基数统计,不做精确统计。日活用户、独立 IP、页面 UV 这种"差几千个也无所谓"的场景完全没问题。但如果是财务对账、库存数量这种一分钱都不能差的数据,千万别碰它。
Bitmaps 适合状态类数据。签到、在线状态、用户性别这种非 0 即 1 的场景,用位图能省出惊人的内存。Bitfields 适合需要原子自增的小整数场景,比如计数器、限流令牌、等级积分。
一句话总结:高级类型不是越高级越好,而是"数据长什么样,就选什么结构去匹配它"。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 五种高级数据类型的核心原理与使用要点
2.1 Stream:消息队列的正确打开方式
Stream 是 Redis 5.0 引入的数据类型,本质是一个追加式的日志结构。每条消息有一个自增 ID,格式是"毫秒时间戳-序号",如果同一毫秒内有多条消息,序号从 0 开始递增。这个设计保证了 ID 是唯一且有序的,消费者可以按 ID 范围拉取消息。
Stream 最核心的优势是消费者组(Consumer Group)。同一个组里的多个消费者会分摊消息,每条消息只会被组内的一个消费者处理。而且 Stream 不像 List 那样取走就删,消息会一直留在内存里,直到你主动 XDEL,或者通过 XTRIM 裁剪旧消息。这就解决了消费者崩溃导致消息丢失的问题——处理完消息之后要主动 XACK,服务端才知道"这条我已经搞定了"。
不过要注意,Stream 的消息持久化依赖 Redis 本身的持久化配置。生产环境如果只开了 RDB 没开 AOF,Redis 一旦重启,最近几秒的 Stream 消息可能就没了。所以用 Stream 做消息队列,至少要把 appendfsync 设成 everysec。
另外,Stream 和常见的"stream disconnected before completion"这类报错没有关系。这个报错通常来自一些 API 客户端或网络中间层,是请求连接被异常中断导致的,不是 Redis Stream 数据类型的问题。排查时要分清网络层错误和数据层错误,别一看到 stream 单词就往数据类型上想。
表:Stream 与 List、Pub/Sub 的对比
| 能力 | List 队列 | Pub/Sub | Stream |
|---|---|---|---|
| 消息持久化 | 是 | 否 | 是 |
| 多消费者组 |
