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)
# 查询前先检查布隆过滤器
# 如果不存在,直接返回空结果,不查数据库