RedisNotes

第 06 章:高级数据类型

zjc 于 2026-01-06 发布

这是《Redis 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 Redis 不只有五大基础类型。Bitmap、HyperLogLog、GEO、Stream 以及 Redis Stack/8.x 的 JSON、Search、TimeSeries,能把很多看似复杂的业务变成几条命令。

6.1 Bitmap:位级空间优化

Bitmap 建立在 String 上,通过 bit offset 操作:

SETBIT sign:user:1001:202608 0 1
GETBIT sign:user:1001:202608 0
BITCOUNT sign:user:1001:202608
BITPOS sign:user:1001:202608 1
BITFIELD sign:user:1001:202608 GET u8 0

每日签到

key: sign:user:{userId}:{yyyyMM}
offset: 日 - 1
value bit: 1 表示签到

连续签到判断可以用 BITCOUNT 与按天 offset 检查;复杂规则可结合业务表或脚本。

活跃用户统计

SETBIT active:20260825 1001 1
BITCOUNT active:20260825
BITOP AND active:both active:20260824 active:20260825
BITOP OR active:any active:20260824 active:20260825

优点:内存极小,1 亿用户约 12MB。限制:要求用户 ID 能稳定映射为 offset,且不适合需要遍历明细的业务。

6.2 HyperLogLog:海量基数估算

HyperLogLog 用极小内存估算集合基数,标准误差约 0.81%:

PFADD page:1001:uv user-a user-b user-c
PFCOUNT page:1001:uv
PFMERGE site:uv page:1:uv page:2:uv

适合:

不适合精确结算、抽奖名单、必须知道具体用户的场景。HLL 只回答“大约多少个”,不保存元素本身。

6.3 GEO:地理位置

GEO 基于 ZSet 与 geohash 编码:

GEOADD shops:city:beijing 116.397128 39.916527 "shop:1001"
GEOADD shops:city:beijing 116.410000 39.920000 "shop:1002"

GEODIST shops:city:beijing shop:1001 shop:1002 km
GEOPOS shops:city:beijing shop:1001
GEOSEARCH shops:city:beijing FROMLONLAT 116.40 39.91 BYRADIUS 3 km ASC COUNT 20

附近门店

1. 写入门店经纬度:GEOADD
2. 用户发起请求:GEOSEARCH 半径/边界 + 排序 + 分页
3. 获取门店 ID 后批量查详情缓存

注意:

6.4 Stream:可靠消息流

Stream 是 Redis 的日志型消息结构,支持消费组:

XADD events:order * orderId 1001 status CREATED amount 99
XLEN events:order
XRANGE events:order - + COUNT 10
XREVRANGE events:order + - COUNT 1

消费组

XGROUP CREATE events:order order-service 0
XREADGROUP GROUP order-service consumer-1 COUNT 10 STREAMS events:order >
XACK events:order order-service 1720000000000-0
XPENDING events:order order-service
XCLAIM events:order order-service consumer-2 60000 1720000000000-0
XTRIM events:order MAXLEN 1000000

核心概念:

概念 含义
entry ID 时间戳-序号,单调递增
> 只读未被该组消费的新消息
pending list 已读未 ack 的消息
consumer 组内具体消费者
XCLAIM 转移超时 pending 消息
MAXLEN 控制日志长度

与 List 队列对比

能力 List Stream
顺序 支持 支持
阻塞读 BLPOP/BRPOP XREAD 阻塞
消费组 不支持 支持
ack/pending 不支持 支持
回放 按位置读
适用 简单任务 事件流/轻量队列

Stream 不是 Kafka 的完整替代:容量受内存限制,持久化与分区伸缩能力也不同。适合中小规模事件流、延迟任务、服务内事件总线。

6.5 JSON(Redis Stack / Redis 8)

JSON 类型来自 RedisJSON 模块,支持路径读写:

JSON.SET product:1001 $ '{"id":1001,"title":"Phone","price":4999,"tags":["new"]}'
JSON.GET product:1001
JSON.GET product:1001 $.price
JSON.SET product:1001 $.price 4899
JSON.ARRAPPEND product:1001 $.tags '"hot"'
JSON.OBJLEN product:1001 $

适用:文档结构、局部路径更新、半结构化配置。如果只用开源核心 Redis,通常退化为 String JSON +业务端反序列化。

6.6 Search 与 TimeSeries

RediSearch

FT.CREATE idx:product ON HASH PREFIX 1 shop:product: SCHEMA title TEXT price NUMERIC
FT.SEARCH idx:product "phone" LIMIT 0 10
FT.SEARCH idx:product "@price:[4000 5000]" SORTBY price

适合轻量搜索、索引过滤和自动补全。复杂搜索、分词、排序权重、大规模检索仍建议 Elasticsearch/OpenSearch。

TimeSeries

TS.CREATE cpu:host:1 RETENTION 86400000
TS.ADD cpu:host:1 * 82.5
TS.RANGE cpu:host:1 - + AGGREGATION avg 60000

适合指标采样、设备监控、轻量时序数据。大规模监控平台仍常用 Prometheus/VictoriaMetrics/TDengine。

6.7 类型复杂度速查

操作 复杂度 风险
GET/SET/HGET O(1)
HSET O(1)
HGETALL O(N) N 大时危险
LPUSH/RPUSH O(1) 单次元素多则 O(M)
LRANGE O(S+N) 全量大列表危险
SADD/SISMEMBER O(1)
SMEMBERS O(N) 大 Set 危险
SINTER O(N*M) 多个大集合危险
ZADD/ZSCORE O(logN)
ZRANGE O(logN+S) 大范围危险
BITCOUNT O(N) 超长 bitmap 注意
PFCOUNT O(1) 常见,合并时增加 通常低

本章小结

思考题

  1. 为什么 HyperLogLog 不能用于抽奖名单?
  2. Stream 的 pending 消息如何避免消费者宕机导致任务丢失?
  3. 如果要实现“每天登录人数 + 近 7 天连续登录人数”,你会组合哪些数据结构?