这是《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
适合:
- 页面 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 后批量查详情缓存
注意:
- 经度在前,纬度在后,容易写反;
- 地球两极与反经线附近查询需业务特殊处理;
- 超大范围搜索会返回大量 member,应限制半径和 COUNT。
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) 常见,合并时增加 | 通常低 |
本章小结
- Bitmap 用极小内存解决签到与活跃位图;
- HyperLogLog 适合海量 UV 估算,不保存具体元素;
- GEO 支持附近位置与距离计算;
- Stream 相比 List 提供消费组、ack、pending 与回放;
- JSON/Search/TimeSeries 属于增强能力,需确认版本与部署支持;
- 高级类型也要评估命令复杂度和大 key 风险。
思考题
- 为什么 HyperLogLog 不能用于抽奖名单?
- Stream 的 pending 消息如何避免消费者宕机导致任务丢失?
- 如果要实现“每天登录人数 + 近 7 天连续登录人数”,你会组合哪些数据结构?