第 3 章:高级数据类型
jerry北京市2026年4月24日Redis 4 次阅读 约 14 分钟

掌握 Bitmap、HyperLogLog、GEO、Stream 四种高级数据类型的原理、命令和实际应用场景。
3.1 Bitmap(位图)
Bitmap 本质上是 String 类型,但可以按位操作。每个 bit 只占 1 位,非常节省内存。
常用命令
# 设置某一位
SETBIT sign:user:1001:202401 0 1 # 第 0 天签到
SETBIT sign:user:1001:202401 1 1 # 第 1 天签到
SETBIT sign:user:1001:202401 4 1 # 第 4 天签到
# 获取某一位
GETBIT sign:user:1001:202401 0 # 1(已签到)
GETBIT sign:user:1001:202401 2 # 0(未签到)
# 统计值为 1 的位数
BITCOUNT sign:user:1001:202401 # 3(签到了 3 天)
# 查找第一个 0 或 1 的位置
BITPOS sign:user:1001:202401 0 # 第一个未签到的日期
BITPOS sign:user:1001:202401 1 # 第一个签到的日期
# 位运算
BITOP AND result key1 key2 # 与运算
BITOP OR result key1 key2 # 或运算
BITOP XOR result key1 key2 # 异或运算
BITOP NOT result key1 # 非运算
应用场景
用户签到:
# 用户 1001 在 2024 年 1 月的签到记录
# 第 0 天 = 1月1日,第 30 天 = 1月31日
SETBIT sign:user:1001:202401 0 1
SETBIT sign:user:1001:202401 1 1
SETBIT sign:user:1001:202401 2 1
# 本月签到天数
BITCOUNT sign:user:1001:202401 # 3
# 连续签到判断:获取所有位,在应用层判断连续性
用户在线状态:
# 用户 ID 作为偏移量
SETBIT online:users 1001 1 # 用户 1001 上线
SETBIT online:users 1001 0 # 用户 1001 下线
GETBIT online:users 1001 # 查询是否在线
BITCOUNT online:users # 在线用户总数
活跃用户统计:
# 每天一个 bitmap
SETBIT active:20240101 1001 1
SETBIT active:20240101 1002 1
SETBIT active:20240102 1001 1
SETBIT active:20240102 1003 1
# 两天都活跃的用户(交集)
BITOP AND active:both active:20240101 active:20240102
BITCOUNT active:both # 1(只有 1001)
# 任意一天活跃的用户(并集)
BITOP OR active:any active:20240101 active:20240102
BITCOUNT active:any # 3
内存优势:1 亿用户的在线状态只需约 12MB。
3.2 HyperLogLog
HyperLogLog 是一种概率数据结构,用于基数估算(去重计数),误差率约 0.81%,每个 key 固定占用 12KB。
常用命令
# 添加元素
PFADD uv:page:home user1 user2 user3
PFADD uv:page:home user1 user4 # user1 重复,不影响
# 获取基数估算值
PFCOUNT uv:page:home # 4(去重后的数量)
# 合并多个 HyperLogLog
PFADD uv:page:about user2 user5
PFMERGE uv:total uv:page:home uv:page:about
PFCOUNT uv:total # 5
应用场景
UV(独立访客)统计:
# 每个页面每天一个 HyperLogLog
PFADD uv:home:20240101 "ip:1.2.3.4"
PFADD uv:home:20240101 "ip:5.6.7.8"
PFADD uv:home:20240101 "ip:1.2.3.4" # 重复访问
PFCOUNT uv:home:20240101 # 2
# 统计一周的 UV(合并 7 天)
PFMERGE uv:home:week uv:home:20240101 uv:home:20240102 ... uv:home:20240107
PFCOUNT uv:home:week
HyperLogLog vs Set:
| 特性 | HyperLogLog | Set |
|---|---|---|
| 内存 | 固定 12KB | 随元素增长 |
| 精确度 | 约 0.81% 误差 | 精确 |
| 适用场景 | 大规模去重计数 | 需要精确值或查询成员 |
3.3 GEO(地理位置)
GEO 底层基于 Sorted Set,将经纬度编码为 Geohash 作为 score 存储。
常用命令
# 添加位置
GEOADD shops 116.403963 39.915119 "天安门"
GEOADD shops 116.397128 39.916527 "故宫"
GEOADD shops 116.384868 39.940023 "鸟巢"
# 获取位置
GEOPOS shops "天安门"
# 116.40396267175674438 39.91511970338637383
# 两点距离
GEODIST shops "天安门" "故宫" km
# "0.7649"
# 搜索附近(Redis 6.2+)
GEOSEARCH shops FROMMEMBER "天安门" BYRADIUS 1 km ASC COUNT 10 WITHCOORD WITHDIST
# 1) "天安门" 0.0000
# 2) "故宫" 0.7649
# 按矩形范围搜索
GEOSEARCH shops FROMMEMBER "天安门" BYBOX 2 2 km ASC
# 获取 Geohash
GEOHASH shops "天安门"
# "wx4g0f6f2v0"
应用场景
附近的人/店铺:
# 添加用户位置
GEOADD user:locations 116.403963 39.915119 "user:1001"
GEOADD user:locations 116.397128 39.916527 "user:1002"
# 查找 user:1001 附近 5km 内的用户
GEOSEARCH user:locations FROMMEMBER "user:1001" BYRADIUS 5 km ASC COUNT 20 WITHDIST
外卖/打车距离计算:
# 计算骑手到用户的距离
GEODIST riders "rider:001" "user:1001" m
3.4 Stream(消息流)
Redis 5.0 引入的数据类型,专门用于消息队列,支持消费组、消息确认、持久化。
基本命令
# 发送消息(* 表示自动生成 ID)
XADD mystream * name "Alice" action "login"
XADD mystream * name "Bob" action "purchase"
# 读取消息
XRANGE mystream - + # 获取所有消息
XRANGE mystream - + COUNT 10 # 获取前 10 条
XLEN mystream # 消息数量
XREAD COUNT 5 BLOCK 0 STREAMS mystream 0 # 从头读取,阻塞等待新消息
消费组
# 创建消费组(从头开始消费)
XGROUP CREATE mystream group1 0
# 创建消费组(只消费新消息)
XGROUP CREATE mystream group2 $
# 消费者读取消息
XREADGROUP GROUP group1 consumer1 COUNT 1 BLOCK 0 STREAMS mystream >
# 确认消息(ACK)
XACK mystream group1 1234567890-0
# 查看待确认消息(PEL)
XPENDING mystream group1 - + 10
# 转移超时未确认的消息给其他消费者
XCLAIM mystream group1 consumer2 3600000 1234567890-0
应用场景
可靠消息队列:
# 生产者
XADD orders * order_id "10001" user_id "u001" amount "99.9"
# 消费组
XGROUP CREATE orders order-processors 0
# 消费者 1
XREADGROUP GROUP order-processors worker1 COUNT 1 BLOCK 5000 STREAMS orders >
# 处理完成后确认
XACK orders order-processors 1234567890-0
# 消费者 2(同组的另一个消费者,负载均衡)
XREADGROUP GROUP order-processors worker2 COUNT 1 BLOCK 5000 STREAMS orders >
Stream vs List 做消息队列:
| 特性 | Stream | List |
|---|---|---|
| 消费组 | ✅ | ❌ |
| 消息确认 | ✅(XACK) | ❌ |
| 消息回溯 | ✅(可重复消费) | ❌(弹出即删除) |
| 阻塞读取 | ✅ | ✅(BRPOP) |
| 消息 ID | ✅(自动生成) | ❌ |
3.5 面试要点
-
Bitmap 的底层实现?
- 本质是 String,按位操作,1 亿用户只需 12MB
-
HyperLogLog 的误差率和内存?
- 误差约 0.81%,固定 12KB
-
GEO 的底层实现?
- 基于 Sorted Set,经纬度编码为 Geohash 作为 score
-
Stream 和 List 做消息队列的区别?
- Stream 支持消费组、消息确认、消息回溯
- List 弹出即删除,不支持消费确认
-
什么场景用 HyperLogLog 而不是 Set?
- 大规模去重计数且允许少量误差(如 UV 统计)
练习
- 使用 Bitmap 实现用户月度签到功能
- 使用 HyperLogLog 统计网站每日 UV
- 使用 GEO 实现"附近的餐厅"功能
- 使用 Stream + 消费组实现一个可靠的订单处理队列
评论
登录 后发表评论
暂无评论