目录

一、Redis Stack 是什么?解决了什么问题?

二、RedisJSON:让 Redis 变成文档数据库

1. 传统做法有多别扭?

2. RedisJSON 的核心能力

3. 实战命令与 Java 代码

4. 优缺点分析

三、RediSearch:轻量搜索引擎

1. 原理简述

2. 创建索引与查询

3. Java 集成

4. 优缺点分析

四、RedisTimeSeries:监控与 IoT 的最佳拍档

1. 为什么不用 ZSET 或 Stream?

2. 命令行实战

3. Java 集成

4. 优缺点分析

五、RedisBloom:高空间效率去重与穿透防护

1. 原理图解

2. 命令行实战

3. Java 集成:缓存穿透防护

4. 优缺点分析

六、组合实战:用 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.SETJSON.GETJSON.DEL,以及带路径的原子化更新 JSON.NUMINCRBYJSON.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. 原理图解

布隆过滤器特性:

  1. 判定不存在:结果 100% 准确,元素一定不在集合
  2. 判定存在:仅为 “可能存在”,存在哈希碰撞导致的误判,无漏判
  3. 原理:多个哈希函数将元素映射到位图多个 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,避免引入新存储。

最后,如有改进之处欢迎指正,觉得有帮助的话不妨点赞收藏支持一下,感谢!

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐