【Redis合集-08】Redis Stack 扩展功能实战:JSON 文档、全文搜索与时序数据处理的一站式高并发利器
目录
四、RedisTimeSeries:监控与 IoT 的最佳拍档
六、组合实战:用 Redis Stack 搭建商品搜索与信息中心
Redis Stack,也就是 Redis 官方出的“全家桶”。以前我们用 Redis 可能就是缓存、队列、分布式锁,但当业务需要存个 JSON、做个全文搜索、记录个时间序列数据时,往往要引入 MongoDB、ES、InfluxDB 等,架构瞬间变重。Redis Stack 把几个最强的扩展模块内置进来,让我们可以继续用 Redis 的高性能,同时处理更多数据模型。下面就把几个核心模块的实战用法记录下来。
一、Redis Stack 是什么?解决了什么问题?
Redis Stack = Redis Server + 精选扩展模块(默认集成)+ RedisInsight 可视化管理工具。
核心模块包括:
-
RedisJSON:原生 JSON 文档存储,支持快速读写和路径操作。
-
RediSearch:全文搜索与二级索引,性能强。
-
RedisTimeSeries:专门为时间序列数据优化的数据结构,支持降采样和聚合。
-
RedisBloom:布隆过滤器和布谷鸟过滤器,高空间效率去重。
-
RedisGraph(可选):图数据库模块,使用的少,这里不展开。
为什么需要这些?
举个例子,一个电商系统用 Redis 做缓存,但还要存储商品详情(JSON)、做商品名的模糊搜索、记录每小时的访问量。以前是 Redis + MongoDB + ES + Influx,现在一组 Redis Stack 就能全搞定,运维成本大幅降低,而且延时低,当然大型系统还是建议拆分组件单独部署。
Redis Stack 轻量化一体化方案适合中小体量业务,显著缩减机器、运维、学习成本;超大规模高并发场景仍需组件解耦,通过独立 Redis 缓存、ES 检索、Mongo 文档、Influx 时序集群实现资源隔离、性能分层、故障互不扩散,支撑更高业务上限。
二、RedisJSON:让 Redis 变成文档数据库
1. 传统做法有多别扭?
以前存 JSON 只能序列化成字符串,想改里面的某个字段(比如库存数量),必须全量取出来、反序列化、修改、再序列化写回,原子性也难以保证。
2. RedisJSON 的核心能力
-
将 JSON 文档直接以树形结构存储在 Redis 里,每个叶子节点都独立可操作。
-
支持
JSON.SET、JSON.GET、JSON.DEL,以及带路径的原子化更新JSON.NUMINCRBY、JSON.ARRAPPEND等。 -
读写效率极高,因为是原生结构,不需要全量序列化/反序列化。
序列化存储 vs 原生 JSON 对比:
传统 String 方式:
SET product:1001 '{"name":"iPhone","price":6999,"stock":200}'
GET product:1001 → 全量字符串 → 反序列化 → 修改 → 序列化 → 全量写回
RedisJSON 方式:
JSON.SET product:1001 $ '{"name":"iPhone","price":6999,"stock":200}'
JSON.NUMINCRBY product:1001 $.stock -1 → 只改 stock 字段,原子操作
JSON.GET product:1001 $.price → 只返回 price
3. 实战命令与 Java 代码
命令行示例:
JSON.SET product:1001 $ '{"name":"HUAWEI","price":6999,"stock":200,"tags":["5G","A16"]}'
JSON.GET product:1001 $.price # 获取价格
JSON.NUMINCRBY product:1001 $.stock -1 # 库存减1,原子操作
JSON.ARRAPPEND product:1001 $.tags '"iOS17"' # 添加标签
Java 集成:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.4.3</version>
</dependency>
// 1. 创建一个连接到本地 Redis Stack 的统一客户端
try (UnifiedJedis jedis = new UnifiedJedis("redis://localhost:6379")) {
// 2. 构造一个 JSONObject 作为商品数据
JSONObject product = new JSONObject()
.put("name", "iPhone 15")
.put("price", 6999)
.put("stock", 200);
// 3. 将 JSON 对象写入 Redis 键 product:1001,路径 $ 代表根
jedis.jsonSet("product:1001", new Path("$"), product);
// 4. 只读取 price 字段,返回的是数组字符串,如 [6999]
String price = jedis.jsonGet("product:1001", String.class, new Path("$.price"));
System.out.println("Price: " + price);
// 5. 原子地扣减库存,-1 表示减 1
jedis.jsonNumIncrBy("product:1001", new Path("$.stock"), -1);
// 6. 向 tags 数组追加新元素,注意字符串要包含 JSON 双引号
jedis.jsonArrAppend("product:1001", new Path("$.tags"), "\"iOS17\"");
}
4. 优缺点分析
| 优点 | 缺点 |
|---|---|
| ✅ 原子操作部分字段,避免并发覆盖 | ❌ 复杂查询能力不如 MongoDB(无多级嵌套查询、聚合管道等) |
| ✅ 无需全量序列化,读写性能极高(亚毫秒) | ❌ 存储结构固定为 JSON,不适合二进制大文件 |
| ✅ 与 Redis 生态无缝集成,运维成本低 | ❌ 内存占用可能较高,需关注大 key |
| ✅ 支持路径表达式,灵活获取/修改深层字段 | ❌ 没有事务中跨文档操作的能力 |
三、RediSearch:轻量搜索引擎
1. 原理简述
RediSearch 在 Redis 内部构建了倒排索引,支持全文搜索、模糊匹配、数字范围过滤、排序、聚合等。索引是异步更新(插入文档时自动索引),性能极高,单节点轻松支撑数万 QPS 的搜索请求。
2. 创建索引与查询
场景:搜索商品,字段有 name(文本)、price(数字)、category(标签)。
创建索引:
# 创建全文索引 idx:product,作用于所有以 "product:" 开头的 JSON 键
FT.CREATE idx:product ON JSON PREFIX 1 product: SCHEMA
# 将 JSON 中的 $.name 映射为索引字段 name,类型为全文 TEXT,权重 5.0
$.name AS name TEXT WEIGHT 5.0
# 将 $.price 映射为 NUMERIC 类型,可排序和范围过滤
$.price AS price NUMERIC SORTABLE
# 将 $.category 映射为 TAG 类型,适合精确匹配和分组
$.category AS category TAG SORTABLE
插入数据并且搜索:
# 插入两个商品文档,文档会自动被索引
JSON.SET product:1001 $ '{"name":"iPhone 15 Pro Max","price":9999,"category":"手机"}'
JSON.SET product:1002 $ '{"name":"华为Mate 60 Pro","price":6999,"category":"手机"}'
JSON.SET product:2001 $ '{"name":"AirPods Pro","price":1799,"category":"耳机"}'
# 在 idx:product 索引中搜索包含 "iPhone" 的文档
FT.SEARCH idx:product "iPhone"
# 组合搜索:分类为 "手机" 且价格在 5000 到 10000 之间
FT.SEARCH idx:product "@category:{手机} @price:[5000 10000]"
# 搜索 "Pro" 并只返回 name 和 price 字段
FT.SEARCH idx:product "Pro" RETURN 3 $.name $.price
# 按 category 聚合统计文档数量
FT.AGGREGATE idx:product "*" GROUPBY 1 @category REDUCE COUNT 0 AS count
3. Java 集成
UnifiedJedis jedis = new UnifiedJedis("redis://localhost:6379");
// 1. 创建索引配置对象
FTCreateParams params = FTCreateParams.createParams()
.on(IndexDataType.JSON) // 索引类型为 JSON
.prefix("product:"); // 只对键前缀为 "product:" 的文档自动索引
// 2. 定义索引字段
Field nameField = Field.text("$.name") // 从 JSON 路径 $.name 取值
.as("name") // 索引中命名为 name
.weight(5.0); // 设置搜索权重
Field priceField = Field.numeric("$.price") // 数值字段
.as("price")
.sortable(true); // 允许排序
Field categoryField = Field.tag("$.category") // 标签字段
.as("category")
.sortable(true);
// 3. 执行创建索引命令
jedis.ftCreate("idx:product", params, nameField, priceField, categoryField);
// 4. 插入 JSON 文档(自动触发索引更新)
jedis.jsonSet("product:1001", new Path("$"),
new JSONObject().put("name", "iPhone 15").put("price", 9999).put("category", "手机"));
// 5. 执行搜索:分类为 "手机" 且价格 5000~10000
SearchResult result = jedis.ftSearch("idx:product", "@category:{手机} @price:[5000 10000]");
// 6. 遍历搜索结果文档
for (Document doc : result.getDocuments()) {
System.out.println("ID: " + doc.getId() + ", Name: " + doc.get("$.name"));
}
4. 优缺点分析
| 优点 | 缺点 |
|---|---|
| ✅ 搜索性能极高,内存操作,延迟低 | ❌ 复杂分词器和自定义分析器弱于 Elasticsearch |
| ✅ 与 Redis 数据紧耦合,无需额外同步 | ❌ 海量文档(亿级)下内存成本高 |
| ✅ 自动索引,支持 JSON 路径映射 | ❌ 索引重建需谨慎,可能影响线上服务 |
| ✅ 内置聚合、排序、分页,满足多数搜索需求 | ❌ 不支持父子文档、嵌套对象等高级建模 |
四、RedisTimeSeries:监控与 IoT 的最佳拍档
1. 为什么不用 ZSET 或 Stream?
-
ZSET 可以用时间戳做 score 存储时序数据,但降采样(一小时平均值)、聚合需要客户端计算。
-
RedisTimeSeries 内置了高效的压缩算法(双 Delta 压缩),支持快速的范围查询和聚合,而且可以定义保留策略自动删除旧数据。
时序存储压缩示意图:
原始数据点 (timestamp: value)
(100, 23.5) (110, 23.6) (120, 23.5) (130, 23.7) ...双 Delta 压缩后:
存储基准值 + 时间间隔 + 变化量
内存占用大幅减少保留策略:自动删除 1 天前的数据,避免内存无限增长
2. 命令行实战
# 创建一个时间序列键 temperature:room1,数据保留 1 天(86400000 毫秒)
TS.CREATE temperature:room1 RETENTION 86400000
# 插入第一个数据点,* 表示使用服务器当前时间戳,值为 23.5
TS.ADD temperature:room1 * 23.5
# 插入第二个数据点,指定时间戳 1620000000,值为 24.0
TS.ADD temperature:room1 1620000000 24.0
# 查询时间范围 [1620000000, 1620003600] 内的数据,并按 60 秒窗口取平均值
TS.RANGE temperature:room1 1620000000 1620003600 AGGREGATION avg 60000
3. Java 集成
// 1. 创建 Jedis 连接
Jedis jedis = new Jedis("localhost", 6379);
// 2. 发送 TS.CREATE 命令,创建时间序列并设置保留时间
jedis.sendCommand("TS.CREATE", "temperature:room1", "RETENTION", "86400000");
// 3. 发送 TS.ADD 命令,* 表示当前时间,值为 23.5
jedis.sendCommand("TS.ADD", "temperature:room1", "*", "23.5");
// 4. 计算查询范围:当前时间 - 2 小时 和 当前时间
String from = String.valueOf(System.currentTimeMillis() - 7200000);
String to = String.valueOf(System.currentTimeMillis());
// 5. 执行范围查询,按 60 秒聚合取平均值
List<Object> result = (List<Object>) jedis.sendCommand("TS.RANGE",
"temperature:room1", from, to, "AGGREGATION", "avg", "60000");
// 6. 结果是一个 List,每一项是 [timestamp, value] 的 List,需要遍历解析
for (Object item : result) {
List<Object> point = (List<Object>) item;
System.out.println("Time: " + point.get(0) + ", Avg: " + point.get(1));
}
4. 优缺点分析
| 优点 | 缺点 |
|---|---|
| ✅ 压缩率高,内存占用低 | ❌ 不支持连续查询(Continuous Query)和自定义函数 |
| ✅ 聚合函数丰富,支持多种降采样 | ❌ 查询语法相对固定,无法做复杂数据处理 |
| ✅ 保留策略自动化管理数据生命周期 | ❌ 数据持久化依赖 RDB/AOF,恢复速度可能不如专业时序库 |
| ✅ 集成在 Redis 中,运维简单 | ❌ 集群分片下跨 slot 的时间序列关联查询受限 |
五、RedisBloom:高空间效率去重与穿透防护
1. 原理图解

布隆过滤器特性:
- 判定不存在:结果 100% 准确,元素一定不在集合
- 判定存在:仅为 “可能存在”,存在哈希碰撞导致的误判,无漏判
- 原理:多个哈希函数将元素映射到位图多个 bit 位,存入时全部置 1;校验时只要有一位为 0,直接判定不存在。
2. 命令行实战
# 创建一个布隆过滤器 user_filter,误判率 1%,预计元素数量 100 万
BF.RESERVE user_filter 0.01 1000000
# 向过滤器添加元素 "user_123"
BF.ADD user_filter "user_123"
# 检查元素 "user_456" 是否可能存在(1 可能存在,0 一定不存在)
BF.EXISTS user_filter "user_456"
3. Java 集成:缓存穿透防护
// 1. 初始化布隆过滤器:名称 "product_filter",误判率 0.1%,预计 100 万元素
jedis.sendCommand("BF.RESERVE", "product_filter", "0.001", "1000000");
// 2. 预热阶段:把数据库中所有商品 ID 加入过滤器
for (String id : productIdList) {
jedis.sendCommand("BF.ADD", "product_filter", id); // 添加每个 ID
}
// 3. 查询接口中使用布隆过滤器
public Product getProduct(String productId) {
// 3.1 检查 ID 是否可能存在
Object exists = jedis.sendCommand("BF.EXISTS", "product_filter", productId);
if (!exists.equals(1L)) {
return null; // 一定不存在,直接返回,避免穿透
}
// 3.2 查询 Redis 缓存(可能是 RedisJSON 数据)
String json = jedis.jsonGet("product:" + productId);
if (json != null) {
return parseProduct(json); // 缓存命中,解析返回
}
// 3.3 缓存未命中,加分布式锁回源数据库(此处省略锁逻辑)
Product product = queryFromDB(productId);
if (product != null) {
jedis.jsonSet("product:" + productId, new Path("$"), toJson(product));
}
return product;
}
4. 优缺点分析
| 优点 | 缺点 |
|---|---|
| ✅ 空间效率极高,百万级数据仅需几 MB | ❌ 布隆过滤器有误判率(可调节,但不可消除) |
| ✅ 支持可扩展,动态增加容量 | ❌ 布隆过滤器不支持删除;布谷鸟过滤器支持删除但空间稍大 |
| ✅ 原子操作,高并发安全 | ❌ 初始化时需预估元素数量,扩容需重建 |
| ✅ 完美配合缓存防穿透,性能消耗极低 | ❌ 单独使用时缺乏与其他数据结构的关联 |
六、组合实战:用 Redis Stack 搭建商品搜索与信息中心
假设我们要做一个轻量级的商品检索系统,前提是不引入 ES,只用 Redis Stack下:
-
商品详情:RedisJSON 存储,支持原子库存扣减。
-
搜索与过滤:RediSearch 索引,按名称、价格、分类查询。
-
价格走势:RedisTimeSeries 记录商品历史价格。
-
ID 去重:RedisBloom 缓存穿透保护。
完整架构示意图:

整套系统只需要一个 Redis Stack 实例(或集群),数据一致性、原子性由 Redis 自身保证,比多组件协同简单太多了。
七、选型边界与总结
虽然 Redis Stack 很全能,但它不能完全替代:
-
MongoDB:海量文档存储、复杂事务、大规模数据分析。
-
Elasticsearch:极复杂的文本分析(拼音、同义词、自定义分词器)、大数据量 TB 级别的文档库。
-
InfluxDB:专业时序数据库,高压缩比和连续查询。
Redis Stack 的优势在于:
-
低延迟:亚毫秒级操作。
-
集成简单:一个进程,多种数据模型。
-
功能闭环:非常适合中小规模、高并发、需要实时响应的场景。
生产建议:如果你的系统已经重度使用 Redis,并且需要简单搜索、JSON 存储、监控时序等,强烈推荐升级到 Redis Stack,避免引入新存储。
最后,如有改进之处欢迎指正,觉得有帮助的话不妨点赞收藏支持一下,感谢!
更多推荐



所有评论(0)