【Redis#9】其他数据结构

引言

Redis 除了我们最常用的 String、Hash、List、Set、ZSet(Sorted Set) 这五种基本数据结构外,还提供了很多高级或特殊用途的数据结构/类型 ,它们可以满足更复杂的业务需求。

✅ Redis 的"五大基本数据结构"回顾

类型 特点
String 字符串,可以是文本、数字、二进制等
Hash 键值对集合,适合存储对象
List 有序的字符串列表(底层为链表)
Set 无序且不重复的字符串集合
ZSet (Sorted Set) 带有分数排序的集合

一、Stream(流)

Redis 5.0 引入,是一个日志型数据结构 ,支持发布订阅、持久化、消费者组 等功能。

可以把 Redis Stream 想象成一个:持久化的、可查询的、支持消费者组的消息队列系统。 基于 Radix Tree + Listpacks(或称 ziplist) 实现的,具有高效写入和读取的能力。

1. Stream 的核心概念

概念 说明
Stream 存储事件记录的有序队列,每个记录都有唯一 ID
Entry / Message 一条消息,包含多个字段(field-value 对)
Consumer Group 消费者组,类似 Kafka 的 consumer group,用于多消费者协作消费
Consumer 消费者,属于某个消费者组
Pending Entries List (PEL) 记录已发给消费者但尚未确认的消息
Claim 把未确认的消息重新分配给其他消费者

比如:我们可以用 Stream 实现一个事件,如下:消防员 + 着火事件

事件 操作
着火事件 Producer 使用XADD写入一条事件消息
消防员 Consumer 属于某个消费者组,监听这个 Stream
发现火情 Consumer 通过XREADGROUP接收到事件
使用干粉灭火器 Consumer 执行业务逻辑(如写数据库、发通知)
确认火已灭 Consumer 调用XACK确认事件处理完成
没火时等待 使用BLOCK参数阻塞等待新事件到来

2. Stream 支持的功能📦

功能 描述
📥 消息写入 使用XADD命令添加新消息
📤 消息读取 使用XRANGE,XREAD,XREADGROUP等命令读取消息
🔄 消费者组 支持多个消费者组并行消费,避免重复消费
🧾 自动偏移量管理 消费者组会自动维护消费进度(offset)
⏱️ 阻塞读取 类似 BLPOP,使用XREADXREADGROUP COUNT ... BLOCK
🔁 消息重试机制 可通过XCLAIM将失败的消息重新分发给其他消费者
🗑️ 消息过期 可设置最大条数限制(MAXLEN)实现自动清理
🔐 持久化支持 消息写入后会持久化到 AOF 和 RDB 中(如果启用)

3. 常用命令一览🛠️

命令 用途
XADD key [MAXLEN ~ count] * field value [field value ...] 添加消息
XRANGE key start end [COUNT count] 查询指定范围内的消息
XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] id [id ...] 从指定位置读取消息
XREADGROUP GROUP group consumer [COUNT count] [BLOCK ms] STREAMS key [key ...] IDLE [min-idle-time] 以消费者组方式读取消息
XACK key group id [id ...] 确认消息已被成功处理
XCLAIM key group consumer min-idle-time id [id ...] 抢占未被确认的消息
XDEL key id [id ...] 删除指定消息
XTRIM key MAXLEN ~ count 控制 stream 的长度(保留最近的 count 条消息)
XGROUP CREATE key group-name $ 创建消费者组
XGROUP SETID key group new-id 设置消费者组的起始读取位置
XGROUP DELGROUP key group 删除消费者组
XINFO STREAM key 查看 stream 的详细信息
XINFO GROUPS key 查看所有消费者组
XINFO CONSUMERS key group 查看某组下的所有消费者

4. 示例操作🔁

1️⃣ 添加消息
bash 复制代码
XADD mystream * name Alice age 30
# 输出
"1717986912345-0"
  • 每条消息都会有一个自动生成的 ID:<时间戳>-<序列号>
2️⃣ 查询所有消息
bash 复制代码
XRANGE mystream - +
# 输出
1) 1) "1717986912345-0"
   2) 1) "name"
      2) "Alice"
      3) "age"
      4) "30"
  • - 表示最小 ID,+ 表示最大 ID
3️⃣ 创建消费者组
bash 复制代码
XGROUP CREATE mystream mygroup $
  • $ 表示从最后一条消息之后开始消费
4️⃣ 以消费者组方式消费消息
bash 复制代码
XREADGROUP GROUP mygroup consumer1 COUNT 1 STREAMS mystream >
  • > 表示只读取未被该组消费过的消息
5️⃣ 确认消息已处理
bash 复制代码
XACK mystream mygroup 1717986912345-0
6️⃣ 查看未确认的消息
bash 复制代码
XPENDING mystream mygroup

5. 使用场景

场景 描述
📬 消息队列 替代 RabbitMQ、Kafka,适用于轻量级消息队列系统
📝 日志收集 收集分布式系统的日志、事件、监控数据
📲 事件溯源(Event Sourcing) 所有状态变更都作为事件流存储
🎮 游戏排行榜更新 记录玩家得分变化事件
📊 实时数据分析 接收实时数据流,进行聚合、分析
🚦 分布式任务调度 多个 worker 协作处理任务流

二、Geospatial(地理位置)

Redis 3.2 引入,用于存储地理位置信息,并支持 距离计算、范围查询 等操作。

Geospatial(地理空间) 是指与地球表面位置相关的信息,通常包括:地理位置(经纬度)、地理对象(点、线、面)、空间关系(距离、包含、交集等)、时间维度(时空变化)

Geospatial 数据可以用于描述任何具有地理属性的事物,比如:城市的位置、道路网络、气象数据、移动设备轨迹、商店分布、用户当前位置

1. Geospatial 技术的核心组成部分🧭

组成部分 描述
GIS(Geographic Information System) 地理信息系统,用于采集、存储、分析和展示地理空间数据
GPS(Global Positioning System) 全球定位系统,用于获取精确的地理位置坐标
RS(Remote Sensing) 遥感技术,通过卫星或无人机获取地表信息
GEOJSON / KML / Shapefile 常见的地理空间数据格式
地图服务(如 Google Maps、高德地图) 提供可视化、导航、搜索等功能

2. Geospatial 应用场景

应用领域 示例
物流配送 实时追踪包裹位置、规划最优路径
共享出行 显示附近车辆、计算距离、预估到达时间
智慧城市 监控交通流量、管理公共设施、应急调度
零售行业 分析门店周边人流、优化选址策略
农业 精准施肥、病虫害监测、产量预测
环境监测 追踪污染源、分析气候变化趋势
社交媒体 发布带地理位置的内容、查找附近好友
游戏开发 构建基于真实世界的 AR 游戏(如 Pokémon GO)

3. 在数据库中支持 Geospatial 的方式🔧

很多数据库都提供了对地理空间数据的支持,下面是一些常见的数据库及其功能:

3.1 Redis GEO

Redis 从 3.2 版本开始支持 GEO 功能 ,可以用来存储地理位置,并进行距离计算和范围查询。

示例

bash 复制代码
# 添加位置
GEOADD cities 116.4074 39.9042 "北京"
GEOADD cities 121.4737 31.2304 "上海"

# 获取某城市的经纬度
GEOPOS cities 北京

# 计算两地之间的距离
GEODIST cities 北京 上海 km

# 查找附近的城市(500km 内)
GEORADIUS cities 116.4074 39.9042 500 km
  • Redis GEO 是基于 Sorted Set + GeoHash 实现的,适合轻量级 LBS(基于位置的服务)应用。
3.2 MySQL Spatial Data Types

MySQL 支持 POINT, LINESTRING, POLYGON 等空间类型,以及空间索引。

示例

mysql 复制代码
CREATE TABLE locations (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100),
    coord POINT SRID 4326
);

INSERT INTO locations (name, coord)
VALUES ('北京', POINT(116.4074, 39.9042));

-- 查询距离北京 500 公里以内的城市
SELECT name, ST_Distance(coord, POINT(116.4074, 39.9042)) AS distance
FROM locations
HAVING distance <= 500;

Geospatial(地理空间)是指与地球位置相关的数据和技术,广泛应用于地图服务、LBS、智慧交通、环境监测等多个领域。主流数据库(如 Redis、MySQL、PostgreSQL、MongoDB)都提供了对地理空间数据的支持,帮助开发者轻松实现位置查询、距离计算、区域划分等功能

三、HyperLogLog

HyperLogLog 是 Redis 提供的一种 概率数据结构(Probabilistic Data Structure) ,用于 高效估算一个集合中不重复元素的数量(基数,Cardinality)

✅ 它是用来做什么的? ==> 场景:你有一个巨大的数据集,想要知道其中有多少个唯一元素(如独立访问用户、IP 数量等),但又不想用 Set 存储所有元素。 【HyperLogLog 就是为此而生的!】

1. 核心特点🔍

特性 描述
用途 基数统计(去重计数)
空间效率极高 最多占用 12KB 内存,可统计上亿个唯一元素
误差率可控 默认误差小于 1%
不可获取具体元素 只能统计数量,不能列出具体内容
支持合并操作 多个 HLL 可以合并,进行全局统计

2. Redis 中 HyperLogLog 的常用命令📦

命令 说明
PFADD key element [element ...] 向指定的 HyperLogLog 中添加元素
PFCOUNT key [key ...] 返回一个或多个 HyperLogLog 的基数估算值
PFMERGE dest source [source ...] 将多个 HyperLogLog 合并为一个

3. 示例操作🔁

bash 复制代码
# 添加元素
PFADD visitors user1 user2 user3 user4 user5

# 查看估计的独立访客数
PFCOUNT visitors
(integer) 5

# 再添加一些新用户
PFADD visitors user6 user7 user3  # user3 已存在

# 再次查看,user3 不会重复计数
PFCOUNT visitors
(integer) 7

# 创建另一个 HyperLogLog
PFADD mobile_visitors user8 user9 user10

# 合并两个 HyperLogLog
PFMERGE all_visitors visitors mobile_visitors

# 查看合并后的总访问人数
PFCOUNT all_visitors
(integer) 10

4. 使用场景

场景 描述
📊 网站 UV 统计 每日/每月独立访客数统计(比使用 Set 节省内存百倍)
🌐 IP 去重计数 统计攻击源 IP、访问来源 IP 的数量
👥 用户行为分析 如"每日活跃用户数"、"不同设备登录数"等
📡 日志聚合 在大数据平台中做轻量级实时统计
🧮 数据预处理 预估去重数据规模,决定是否需要更精确的计算方式

5. 空间效率对比💾

数据结构 存储1万个字符串所需内存 存储100万个字符串所需内存
Set ~几 MB 几百 MB 到 1GB+
HyperLogLog 最多 12KB 始终是 12KB

Redis 的 HLL 实现使用了稀疏和密集两种存储格式,最终统一压缩为最多 12KB。

6. 注意事项⚠️

⚠️ 注意:它只是一个 近似算法 ,不能获取精确值。

问题 建议
是否精确? ❌ 否,是一个近似算法(误差 < 1%)
是否能查具体元素? ❌ 否,只能统计数量
是否适合小数据集? ✅ 也适合,但如果你要完全精确,还是用 Set 更好
是否支持合并? ✅ 支持,这是其一大优势
是否支持持久化? ✅ 是,写入 AOF 和 RDB

小结:HyperLogLog 是 Redis 提供的一个高效估算唯一元素数量的概率数据结构,非常适合用于大规模去重统计(如 UV、IP 数量等),仅需极小内存即可完成超大体量的数据估算,是大数据统计分析中的利器

四、Bitmaps(位图)

Redis 的 Bitmaps 并不是一种独立的数据结构 ,而是基于 String 类型 实现的一种高效操作二进制位(bit)的方式。

它非常适合用于:用户签到系统、日活统计(DAU)、布尔状态记录(如是否已读、是否登录等)、紧凑的去重计数

1. 核心特点

特性 描述
存储方式 基于 String,每个字符是 8 bit
操作粒度 每个 bit 可单独设置、获取、统计
节省内存 1 字节可表示 8 个布尔值,极大节省空间
支持位运算 AND、OR、XOR、NOT 等
适用场景 状态标记、签到、访问统计

2. 常用命令📦

命令 说明
SETBIT key offset value 设置某个 bit 位的值(0 或 1)
GETBIT key offset 获取某个 bit 位的值
BITCOUNT key [start end] 统计被设为 1 的 bit 数量
BITPOS key bit [start] [end] 查找第一个值为指定 bit 的位置
BITOP operation destkey key [key ...] 对多个 bitmap 做位运算(AND/OR/XOR/NOT)

3. 示例操作🔁

3.1 用户签到系统(一个月)
bash 复制代码
# 用户 ID 为 1001,表示他在第 0 天(1号)和第 7 天(8号)签到了
SETBIT user:1001:sign_in 0 1
SETBIT user:1001:sign_in 7 1

# 查询某天是否签到
GETBIT user:1001:sign_in 0   # 返回 (integer) 1
GETBIT user:1001:sign_in 3   # 返回 (integer) 0

# 统计总共签到几天
BITCOUNT user:1001:sign_in   # 返回 (integer) 2
3.2 统计日活跃用户(DAU)

假设你有 100,000 个用户,ID 从 0 到 99999。每天记录一个用户是否登录:

bash 复制代码
# 今天是 2025-04-05
SETBIT dau:20250405 1001 1   # 用户 1001 登录了
SETBIT dau:20250405 2002 1   # 用户 2002 登录了

# 查询总登录人数
BITCOUNT dau:20250405        # 返回 (integer) 2
3.3 多日签到合并统计(BitOp)

你想知道用户 1001 在连续三天内的签到情况:

bash 复制代码
# 第一天签到
SETBIT user:1001:day1 0 1
SETBIT user:1001:day1 2 1

# 第二天签到
SETBIT user:1001:day2 1 1
SETBIT user:1001:day2 2 1

# 合并两天签到情况(按 OR 运算)
BITOP OR user:1001:total_sign user:1001:day1 user:1001:day2

# 查看合并后的签到总数
BITCOUNT user:1001:total_sign   # 返回 (integer) 3 (0,1,2 都为 1)

4. 使用场景

场景 描述
📅 用户签到系统 每个 bit 表示一天是否签到
👥 日活跃用户统计(DAU) 每个 bit 表示一个用户当天是否活跃
📮 已读消息标记 记录用户是否阅读过某条消息
🎮 游戏成就系统 每个 bit 表示一项成就是否完成
🧮 紧凑布尔状态存储 替代多个 key,减少内存开销
📊 数据压缩 用最少的空间表达大量布尔状态

5. 内存占用对比💾

数据类型 1w 个布尔值所需内存 100w 个布尔值所需内存
Hash / Set 几 MB ~ 几十 MB 几百 MB ~ 1GB+
Bitmaps 1250 Bytes ~124KB

💡 一个 BitMap 可以用 12KB 内存来表示超过 10 万个布尔值

⚠️ 注意事项

问题 建议
是否精确 ✅ 是(无误差)
是否支持并发 ✅ 是(Redis 是单线程原子操作)
是否适合小数据集 ✅ 是
是否能查询具体哪些 bit 为 1 ❌ 否(只能统计数量或查找位置)
是否支持压缩 ✅ 是(Redis 内部做了优化)

小结:Redis Bitmaps 是一种基于 String 的高效位操作机制,非常适合用来做签到系统、日活统计、布尔状态管理等场景,仅需极小内存即可处理上百万级别的布尔状态,是 Redis 中非常实用的"隐藏神器"。

五、Bitfields

Bitfields 是 Redis 提供的一种高级数据结构,它允许你在 Redis 的 String 类型中操作二进制位(bit)的多个字段(field) 。它是对 BITFIELD 命令的支持,从 Redis 3.2 版本开始引入。

1. Bitfields 是什么?🧠

Redis 中的 String 是二进制安全的字节数组 ,一个 String 最多可以存储 512MB 的数据。而 BITFIELD 命令允许我们在这些字节中定义多个 "位字段"(bitfield) ,每个字段可以是:

  • 指定长度的 有符号整数(signed integer)
  • 或者无符号整数(unsigned integer)

我们可以对这些字段进行 读取、写入、增减等操作 ,非常适合用于紧凑的数据表示和高效的状态管理。

2. 使用场景

📅 用户签到系统 用 1 bit 表示一天是否签到,365 天只需 46 字节
🎮 游戏角色状态 存储角色属性、技能等级、成就等信息
📊 高频计数器 精确控制字段大小,节省内存空间
🧮 位标志集合 代替多个布尔值,减少 key 数量

3. BITFIELD 常用命令🛠️

3.1 定义并操作多个字段
bash 复制代码
BITFIELD key [GET type pos] [SET type pos val] [INCRBY type pos delta]

参数说明

  • type:字段类型,如:
    • u4 表示 4 位无符号整数(0 ~ 15)
    • i8 表示 8 位有符号整数(-128 ~ 127)
  • pos:字段起始的 bit 位置(从 0 开始)
  • val:要设置的值
  • delta:增量值
3.2 示例操作🔁

① 记录用户每月签到情况(31天)

bash 复制代码
# 设置第0天为已签到(1),第1天未签到(0)
BITFIELD user:1001:sign_in SET u1 0 1 SET u1 1 0

# 查询第0天和第1天签到状态
BITFIELD user:1001:sign_in GET u1 0 GET u1 1

# 输出
1) (integer) 1
2) (integer) 0

② 使用带符号整数操作计数器

bash 复制代码
# 设置第0位为 4 位有符号整数,初始值为 5
BITFIELD user:points SET i4 0 5

# 查询该字段的值
BITFIELD user:points GET i4 0
(integer) 5

# 增加 2
BITFIELD user:points INCRBY i4 0 2
(integer) 7

# 减少 5
BITFIELD user:points INCRBY i4 0 -5
(integer) 2

③ 紧凑存储个状态字段

比如你想存储一个游戏角色的状态:

字段 类型 占用位数 取值范围
HP(生命值) 无符号 10 bits 0 ~ 1023
MP(魔法值) 无符号 8 bits 0 ~ 255
状态标志 有符号 2 bits -2 ~ 1
bash 复制代码
# 初始化 HP=500, MP=200, 状态=-1
BITFIELD player:101 SET u10 0 500 SET u8 10 200 SET i2 18 -1

# 查询所有字段
BITFIELD player:101 GET u10 0 GET u8 10 GET i2 18

# 输出
1) (integer) 500
2) (integer) 200
3) (integer) -1

4. Bitfields 的优势

优点 说明
💾 内存占用极低 比多个 key 更节省内存
⚡ 高效访问 所有操作都在一个 key 中完成
🧮 支持多种数据格式 支持有符号/无符号整数
🔄 原子性操作 支持原子增减,适合并发计数
🧩 结构灵活 可自定义字段大小和偏移量

5. 注意事项⚠️

  • 所有操作都是基于 bit 级别 ,需要自己计算偏移位置。
  • 不支持直接删除某个字段,只能重置整个 key。
  • 如果字段超出范围,会自动截断(wrap around)或溢出。
  • 适用于内部状态存储,不适合频繁查询的业务逻辑。

结论:Redis 的 Bitfields 是一种强大的二进制字段操作工具,它允许你在字符串中按位定义多个字段,并对其进行读写、增减等操作,非常适合用来做状态管理、签到系统、紧凑计数器等高性能、低内存消耗的场景

六、Module 扩展模块

Redis 支持通过加载模块来扩展新的数据结构。例如:

📌 RedisJSON(JSON 数据结构)

  • 存储 JSON 格式数据
  • 支持 JSONPath 查询和修改字段
bash 复制代码
JSON.SET user:1001 $ '{"name":"Tom","age":25}'
JSON.GET user:1001 $.name

📌 RedisTimeSeries(时间序列数据库)

  • 高效存储和查询时间序列数据(如监控指标、传感器数据)
bash 复制代码
TS.CREATE temperature:1 LABELS sensor type temp
TS.ADD temperature:1 * 25.5
TS.RANGE temperature:1 0 -1

📌 其他常见模块

  • RedisGraph(图数据库)
  • RedisSearch(全文搜索 + 聚合)
  • RedisAI(机器学习模型部署)
相关推荐
winfield8212 小时前
Redis 线上问题排查完整手册
redis
言之。2 小时前
Django REST框架:ModelViewSet全面解析
数据库·python·django
-Xie-2 小时前
Mysql杂志(十四)——Mysql逻辑架构
数据库·mysql·架构
西贝爱学习2 小时前
数据库系统概论的第六版与第五版的区别
数据库·oracle
七夜zippoe3 小时前
多级缓存架构实战手册:Caffeine+Redis 从设计到落地的全链路解决方案
redis·缓存·架构
心月狐的流火号3 小时前
Redis 的高性能引擎 Reactor 详解与基于 Go 手写 Redis
redis·后端
nsjqj3 小时前
数据结构:优先级队列(堆)
数据结构
JasmineX-13 小时前
数据结构——顺序表(c语言笔记)
c语言·开发语言·数据结构·笔记
emma羊羊4 小时前
【SQL注入】延时盲注
数据库·sql·网络安全