强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Redis 完全指南 / 16 - Redis Stack

Redis Stack

16.1 Redis Stack 概述

Redis Stack 是 Redis 官方推出的扩展发行版,将 Redis 核心与多个强大模块打包在一起。

Redis Stack 包含的模块

模块功能版本
RedisJSON原生 JSON 数据类型2.x
RediSearch全文搜索、二级索引2.x
RedisGraph图数据库2.x
RedisTimeSeries时序数据1.x
RedisBloom布隆过滤器、概率数据结构2.x

安装 Redis Stack

# Docker 方式(推荐)
docker run -d --name redis-stack \
  -p 6379:6379 \
  -p 8001:8001 \
  redis/redis-stack:latest

# Docker Compose
version: '3.8'
services:
  redis-stack:
    image: redis/redis-stack:latest
    container_name: redis-stack
    ports:
      - "6379:6379"
      - "8001:8001"    # RedisInsight Web UI
    volumes:
      - ./data:/data
    environment:
      REDIS_ARGS: "--requirepass mypassword"
# 验证模块
redis-cli MODULE LIST
# 1) 1) "name"
#    2) "ReJSON"
#    ...
# 2) 1) "name"
#    2) "search"
#    ...

16.2 RedisJSON

RedisJSON 允许在 Redis 中存储、更新和查询 JSON 文档。

基本操作

# 设置 JSON 文档
JSON.SET user:1001 $ '{"name":"张三","age":25,"address":{"city":"北京","district":"朝阳"},"hobbies":["reading","coding"]}'

# 获取整个文档
JSON.GET user:1001
# {"name":"张三","age":25,"address":{"city":"北京","district":"朝阳"},"hobbies":["reading","coding"]}

# 获取特定字段
JSON.GET user:1001 $.name
# ["张三"]

JSON.GET user:1001 $.address.city
# ["北京"]

JSON.GET user:1001 $.hobbies[0]
# ["reading"]

JSON Path 语法

路径说明
$根节点
$.name顶层字段
$.address.city嵌套字段
$.hobbies[0]数组第一个元素
$.hobbies[-1]数组最后一个元素
$.hobbies[0:2]数组切片
$..name递归搜索 name 字段
$.hobbies[*]数组所有元素

更新操作

# 设置字段值
JSON.SET user:1001 $.age 26

# 设置嵌套字段
JSON.SET user:1001 $.address.district "海淀"

# 追加数组元素
JSON.ARRAPPEND user:1001 $.hobbies "swimming"
# (integer) 3

# 数组插入
JSON.ARRINSERT user:1001 $.hobbies 1 "hiking"

# 数组弹出
JSON.ARRPOP user:1001 $.hobbies 0
# "reading"

# 数组长度
JSON.ARRLEN user:1001 $.hobbies

# 删除字段
JSON.DEL user:1001 $.address.district

# 数值自增
JSON.NUMINCRBY user:1001 $.age 1
# [27]

# 字符串操作
JSON.STRLEN user:1001 $.name
JSON.TOGGLE user:1001 $.active    # 布尔值取反

类型检查

JSON.TYPE user:1001 $          # "object"
JSON.TYPE user:1001 $.name     # "string"
JSON.TYPE user:1001 $.age      # "integer"
JSON.TYPE user:1001 $.hobbies  # "array"

JSON.OBJKEYS user:1001 $       # ["name", "age", "address", "hobbies"]
JSON.OBJLEN user:1001 $        # 4

多文档操作

# MGET 获取多个文档的字段
JSON.MGET user:1001 user:1002 $.name
# 1) ["张三"]
# 2) ["李四"]

16.3 RediSearch

RediSearch 提供全文搜索和二级索引功能。

创建索引

# 创建商品索引
FT.CREATE idx:products
    ON HASH
    PREFIX 1 product:
    SCHEMA
        name TEXT WEIGHT 2.0 SORTABLE
        category TAG SORTABLE
        price NUMERIC SORTABLE
        brand TAG SORTABLE
        description TEXT
        in_stock TAG
        created_at NUMERIC SORTABLE

添加数据

# 添加商品(使用 Hash)
HSET product:1001 name "iPhone 15 Pro" category "手机" price 8999 brand "Apple" description "A17 Pro 芯片,钛金属设计" in_stock "true" created_at 1715318400
HSET product:1002 name "MacBook Pro 14" category "电脑" price 16999 brand "Apple" description "M3 Pro 芯片,Liquid Retina XDR" in_stock "true" created_at 1715318500
HSET product:1003 name "华为 Mate 60 Pro" category "手机" price 6999 brand "Huawei" description "麒麟 9000S,卫星通信" in_stock "true" created_at 1715318600
HSET product:1004 name "AirPods Pro" category "耳机" price 1999 brand "Apple" description "主动降噪,自适应透明模式" in_stock "false" created_at 1715318700

全文搜索

# 搜索包含 "Pro" 的商品
FT.SEARCH idx:products "Pro"
# 1) (integer) 3
# 2) "product:1002"
# 3) 1) "name"
#    2) "MacBook Pro 14"
#    ...
# 4) "product:1001"
# ...

# 搜索 "Pro" 并且 category 是 "手机"
FT.SEARCH idx:products "Pro @category:{手机}"
# 1) (integer) 2
# 2) "product:1001"
# 3) "product:1003"

# 按价格排序(从低到高)
FT.SEARCH idx:products "*" SORTBY price ASC

# 按价格范围过滤
FT.SEARCH idx:products "@price:[5000 10000]"

# 多条件过滤
FT.SEARCH idx:products "@category:{手机} @price:[5000 10000] @in_stock:{true}"

# 搜索 "芯片" 并高亮显示
FT.SEARCH idx:products "芯片" HIGHLIGHT FIELDS 1 description TAGS "<b>" "</b>"

# 限制返回字段
FT.SEARCH idx:products "Pro" RETURN 3 name price category

# 分页
FT.SEARCH idx:products "*" LIMIT 0 10
FT.SEARCH idx:products "*" LIMIT 10 10

# 统计
FT.AGGREGATE idx:products "*" GROUPBY 1 @category REDUCE COUNT 0 AS count

# 自动补全
FT.SUGADD autocomplete "iPhone 15" 1
FT.SUGADD autocomplete "iPhone 15 Pro" 2
FT.SUGADD autocomplete "iPhone 15 Pro Max" 3
FT.SUGGET autocomplete "iph"
# 1) "iPhone 15"
# 2) "iPhone 15 Pro"
# 3) "iPhone 15 Pro Max"

索引管理

# 查看索引信息
FT.INFO idx:products

# 列出所有索引
FT._LIST

# 删除索引
FT.DROPINDEX idx:products

# 异步删除索引(不删除文档)
FT.DROPINDEX idx:products DD    # DD = Delete Documents

16.4 RedisGraph

RedisGraph 是基于属性图模型的图数据库。

基本操作

# 创建节点
GRAPH.QUERY social "
  CREATE (alice:Person {name: 'Alice', age: 30})
  CREATE (bob:Person {name: 'Bob', age: 25})
  CREATE (charlie:Person {name: 'Charlie', age: 35})
  CREATE (redis:Tech {name: 'Redis', type: 'database'})
  CREATE (python:Tech {name: 'Python', type: 'language'})
"

# 创建关系
GRAPH.QUERY social "
  MATCH (a:Person {name: 'Alice'}), (b:Person {name: 'Bob'})
  CREATE (a)-[:FRIENDS_WITH {since: 2020}]->(b)
  
  MATCH (a:Person {name: 'Alice'}), (r:Tech {name: 'Redis'})
  CREATE (a)-[:KNOWS]->(r)
  
  MATCH (b:Person {name: 'Bob'}), (p:Tech {name: 'Python'})
  CREATE (b)-[:KNOWS]->(p)
  
  MATCH (c:Person {name: 'Charlie'}), (a:Person {name: 'Alice'})
  CREATE (c)-[:FRIENDS_WITH {since: 2021}]->(a)
"

图查询

# 查询所有 Person 节点
GRAPH.QUERY social "MATCH (p:Person) RETURN p.name, p.age"
# 1) 1) "p.name"
#    2) "p.age"
# 2) 1) 1) "Alice"
#       2) (integer) 30
# 3) 1) 1) "Bob"
#       2) (integer) 25
# 4) 1) 1) "Charlie"
#       2) (integer) 35

# 查询 Alice 的朋友
GRAPH.QUERY social "
  MATCH (a:Person {name: 'Alice'})-[:FRIENDS_WITH]->(friend:Person)
  RETURN friend.name, friend.age
"
# 1) 1) "friend.name"
#    2) "friend.age"
# 2) 1) 1) "Bob"
#       2) (integer) 25

# 查询 Alice 朋友的朋友(两度关系)
GRAPH.QUERY social "
  MATCH (a:Person {name: 'Alice'})-[:FRIENDS_WITH*1..2]->(fof:Person)
  WHERE fof.name <> 'Alice'
  RETURN DISTINCT fof.name
"

# 查询了解某项技术的人
GRAPH.QUERY social "
  MATCH (p:Person)-[:KNOWS]->(t:Tech {name: 'Redis'})
  RETURN p.name
"

# 统计每个 Person 的朋友数量
GRAPH.QUERY social "
  MATCH (p:Person)-[:FRIENDS_WITH]->(friend:Person)
  RETURN p.name, COUNT(friend) AS friend_count
  ORDER BY friend_count DESC
"

# 图统计
GRAPH.QUERY social "MATCH (n) RETURN COUNT(n)"

16.5 RedisTimeSeries

# 创建时间序列
TS.CREATE sensor:temperature RETENTION 86400000 LABELS sensor_id 1 location "office"

# 添加数据点
TS.ADD sensor:temperature 1715318400000 25.5
TS.ADD sensor:temperature 1715318460000 25.8
TS.ADD sensor:temperature 1715318520000 26.1

# 查询范围
TS.RANGE sensor:temperature 1715318400000 1715318520000

# 聚合查询(每分钟平均值)
TS.RANGE sensor:temperature 1715318400000 1715319000000 AGGREGATION avg 60000

# 最新值
TS.GET sensor:temperature

# 多序列查询
TS.MGET FILTER sensor_id=1

16.6 RedisBloom

# 布隆过滤器
BF.RESERVE myfilter 0.001 1000000    # 误判率 0.1%,容量 100 万
BF.ADD myfilter "item1"
BF.ADD myfilter "item2"

# 检查元素是否存在
BF.EXISTS myfilter "item1"      # 1(可能存在)
BF.EXISTS myfilter "item999"    # 0(一定不存在)

# 批量添加
BF.MADD myfilter "item3" "item4" "item5"

# 批量检查
BF.MEXISTS myfilter "item1" "item999"

# 计数布隆过滤器
BF.INFO myfilter

# Cuckoo 过滤器(支持删除)
CF.RESERVE mycuckoo 1000000
CF.ADD mycuckoo "item1"
CF.DEL mycuckoo "item1"
CF.EXISTS mycuckoo "item1"      # 0

布隆过滤器应用场景

# 1. 爬虫 URL 去重
BF.RESERVE crawler:visited 0.001 10000000
BF.ADD crawler:visited "https://example.com/page1"
BF.EXISTS crawler:visited "https://example.com/page1"  # 1 → 已访问

# 2. 防止缓存穿透
BF.RESERVE cache:exists 0.001 10000000
# 写入所有合法 Key
BF.ADD cache:exists "user:1001"
# 查询时先检查布隆过滤器
BF.EXISTS cache:exists "user:99999"  # 0 → Key 一定不存在,直接返回

📌 业务场景

场景一:商品搜索(RediSearch)

# 电商商品全文搜索 + 过滤 + 排序
FT.SEARCH idx:products "@category:{手机} @price:[3000 8000] 苹果" SORTBY price ASC LIMIT 0 20

场景二:社交网络分析(RedisGraph)

# 推荐共同好友
GRAPH.QUERY social "
  MATCH (a:Person {name: 'Alice'})-[:FRIENDS_WITH]->(mutual)-[:FRIENDS_WITH]->(b:Person)
  WHERE NOT (a)-[:FRIENDS_WITH]->(b) AND a <> b
  RETURN b.name, COUNT(mutual) AS mutual_friends
  ORDER BY mutual_friends DESC LIMIT 5
"

场景三:缓存穿透防护(RedisBloom)

# 查询前先检查布隆过滤器
# 如果不存在,直接返回空结果,不查数据库

🔗 扩展阅读