第 3 章:高级数据类型

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

掌握 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 面试要点

  1. Bitmap 的底层实现?

    • 本质是 String,按位操作,1 亿用户只需 12MB
  2. HyperLogLog 的误差率和内存?

    • 误差约 0.81%,固定 12KB
  3. GEO 的底层实现?

    • 基于 Sorted Set,经纬度编码为 Geohash 作为 score
  4. Stream 和 List 做消息队列的区别?

    • Stream 支持消费组、消息确认、消息回溯
    • List 弹出即删除,不支持消费确认
  5. 什么场景用 HyperLogLog 而不是 Set?

    • 大规模去重计数且允许少量误差(如 UV 统计)

练习

  1. 使用 Bitmap 实现用户月度签到功能
  2. 使用 HyperLogLog 统计网站每日 UV
  3. 使用 GEO 实现"附近的餐厅"功能
  4. 使用 Stream + 消费组实现一个可靠的订单处理队列

← 上一章:五大基础数据类型 | 下一章:键管理与过期策略 →

评论

登录 后发表评论

暂无评论