一、Redis产品体系全景图
1. Redis产品家族概览
目前Redis官方提供以下核心产品:
| 产品名称 | 定位 | 适用场景 |
|---|---|---|
| Redis Cloud | 云托管服务 | 企业级云原生部署 |
| Redis Enterprise | 企业版 | 高可用、多租户企业级需求 |
| Redis OSS | 开源版 | 基础缓存和数据结构存储 |
| Redis Stack | 扩展增强版 | 需要JSON、搜索等高级功能的场景 |
| Redis Insight | 图形化管理工具 | 监控、调试和运维 |
2. Redis Stack的核心价值
Redis Stack = Redis OSS + 扩展模块 + 增强功能,为开发者提供了开箱即用的增强版Redis,特别适合以下场景:
-
需要原生JSON支持的Web应用
-
需要全文搜索功能的电商平台
-
需要高效去重功能的大数据系统
二、快速体验:申请免费Redis Cloud实例
步骤一:注册账号
访问Redis官网,使用GitHub、Google等第三方账号快速注册。
步骤二:创建免费实例
注册后系统会自动分配一个免费的Redis Stack实例,支持所有扩展功能。
步骤三:连接使用
# 使用redis-cli连接(示例地址)
redis-cli -h redis-17998.c295.ap-southeast-1-1.ec2.redis.s-1oud.com -p 17998 -a yourpassword
注意:免费实例有容量和时长限制,生产环境建议使用付费版本(最低5美元/月)。
三、Redis Stack核心扩展详解
1. Redis JSON:原生的JSON数据处理
什么是Redis JSON?
Redis JSON是Redis的扩展模块,提供对JSON数据的原生支持,可以直接在Redis中存储、查询和操作JSON文档。
核心命令示例
# 设置JSON数据
127.0.0.1:6379> JSON.SET user $ '{"name":"loulan","age":18}'
OK
# 查询JSON数据
127.0.0.1:6379> JSON.GET user $
"{\"name\":\"loulan\",\"age\":18}"
# 查询特定字段
127.0.0.1:6379> JSON.GET user $.name
"[\"loulan\"]"
# 修改数值字段
127.0.0.1:6379> JSON.NUMINCRBY user $.age 2
"[20]"
# 添加新字段(仅当不存在时)
127.0.0.1:6379> JSON.SET user $.address '{"city":"Changsha","country":"China"}' NX
OK
# 数组操作
127.0.0.1:6379> JSON.SET user $.hobbies '["reading"]'
OK
127.0.0.1:6379> JSON.ARRAPPEND user $.hobbies '"swimming"'
"[2]"
# 删除字段
127.0.0.1:6379> JSON.DEL user $.address
(integer) 1
Redis JSON的三大优势
-
性能卓越:采用二进制格式存储,读写性能媲美MongoDB和Elasticsearch
-
存储高效:树状存储结构,支持快速访问子元素
-
生态完整:完美集成TTL、事务、发布/订阅等Redis原生功能
2. Search and Query:Redis中的搜索引擎
传统Scan的局限性
# Scan基础用法
127.0.0.1:6379> SCAN 0 MATCH user:* COUNT 10
1) "18" # 下次迭代的游标
2) 1) "user:1"
2) "user:2"
3) "user:3"
# 批量创建测试数据
127.0.0.1:6379> EVAL 'for i = 1,30,1 do redis.call("SET","k"...tostring(i),"v"...tostring(i)) end' 0
传统Scan只能基于Key的模式匹配,无法进行复杂的内容搜索。
RedisSearch:Elasticsearch的Redis平替方案
创建商品搜索索引
# 清空数据
127.0.0.1:6379> FLUSHALL
OK
# 创建基于JSON的商品索引
127.0.0.1:6379> FT.CREATE productIndex ON JSON SCHEMA $.name AS name TEXT $.price AS price NUMERIC
OK
# 批量添加商品数据
127.0.0.1:6379> JSON.SET phone:1 $ '{"id":1,"name":"HUAWEI 1","description":"HUAWEI PHONE 1","price":1999}'
OK
127.0.0.1:6379> JSON.SET phone:2 $ '{"id":2,"name":"HUAWEI 2","description":"HUAWEI PHONE 2","price":2999}'
OK
# ... 添加更多数据
# 复杂条件搜索:名称包含HUAWEI,价格在1000-5000之间
127.0.0.1:6379> FT.SEARCH productIndex "name:HUAWEI @price:[1000 5000]" RETURN 2 id name
1) (integer) 4 # 匹配的数量
2) "phone:1"
3) 1) "id"
2) "1"
3) "name"
4) "HUAWEI 1"
4) "phone:2"
5) 1) "id"
2) "2"
3) "name"
4) "HUAWEI 2"
# ... 更多结果
索引信息查询
# 查看索引状态
127.0.0.1:6379> FT.INFO productIndex
1) "index_name"
2) "productIndex"
3) "index_definition"
4) 1) "key_type"
2) "JSON"
3) "prefixes"
4) (empty array)
5) "default_score"
6) "1"
# ... 更多信息
3. Bloom Filter:海量数据去重利器
布隆过滤器原理
-
数据结构:位数组 + 多个哈希函数
-
核心特性:
-
判断"不存在":100%准确
-
判断"存在":可能存在误判(假阳性)
-
无法删除元素
-
空间效率极高
-
Guava本地布隆过滤器示例
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>33.1.0</version>
</dependency>
public class GuavaBloomFilterDemo {
public static void main(String[] args) {
// 创建布隆过滤器:容量10000,误判率1%
BloomFilter<String> bloomFilter = BloomFilter.create(
Funnels.stringFunnel(StandardCharsets.UTF_8),
10000,
0.01
);
// 添加A-Z
for (int i = 65; i <= 90; i++) {
bloomFilter.put(String.valueOf((char) i));
}
// 测试
System.out.println(bloomFilter.mightContain("A")); // true
System.out.println(bloomFilter.mightContain("a")); // false(大小写敏感)
}
}
Redis布隆过滤器实战
# 创建布隆过滤器(容量1000,误判率1%,不自动扩容)
127.0.0.1:6379> BF.RESERVE userFilter 0.01 1000 NONSCALING
OK
# 添加单个元素
127.0.0.1:6379> BF.ADD userFilter user123
(integer) 1
# 批量添加元素
127.0.0.1:6379> BF.MADD userFilter user456 user789 user101112
1) (integer) 1
2) (integer) 1
3) (integer) 1
# 判断元素是否存在
127.0.0.1:6379> BF.EXISTS userFilter user123
(integer) 1
127.0.0.1:6379> BF.EXISTS userFilter notexist
(integer) 0
# 批量判断
127.0.0.1:6379> BF.MEXISTS userFilter user123 notexist user456
1) (integer) 1
2) (integer) 0
3) (integer) 1
# 查看过滤器信息
127.0.0.1:6379> BF.INFO userFilter
1) Capacity
2) (integer) 1000
3) Size
4) (integer) 2876
5) Number of filters
6) (integer) 1
7) Number of items inserted
8) (integer) 4
9) Expansion rate
10) (integer) 0
# 扫描布隆过滤器(用于备份)
127.0.0.1:6379> BF.SCANDUMP userFilter 0
1) (integer) 1
2) "\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80"
4. Cuckoo Filter:可删除的布隆过滤器
Cuckoo Filter vs Bloom Filter
| 特性 | Bloom Filter | Cuckoo Filter |
|---|---|---|
| 删除支持 | ❌ 不支持 | ✅ 支持 |
| 空间效率 | 较高 | 更高 |
| 误判率 | 可配置 | 可配置 |
| 实现复杂度 | 简单 | 复杂 |
Cuckoo Filter使用示例
# 创建Cuckoo Filter(容量1000,桶大小2,最大迭代20次,扩容系数1)
127.0.0.1:6379> CF.RESERCE userCuckooFilter 1000 BUCKETSIZE 2 MAXITERATIONS 20 EXPANSION 1
OK
# 添加元素
127.0.0.1:6379> CF.ADD userCuckooFilter user123
(integer) 1
# 判断元素是否存在
127.0.0.1:6379> CF.EXISTS userCuckooFilter user123
(integer) 1
# 删除元素(Bloom Filter不支持)
127.0.0.1:6379> CF.DEL userCuckooFilter user123
(integer) 1
# 删除后再次判断
127.0.0.1:6379> CF.EXISTS userCuckooFilter user123
(integer) 0
四、手动安装Redis扩展模块
步骤一:下载扩展模块
-
访问Redis Cloud下载中心
-
选择对应Redis版本和操作系统
-
下载.so扩展文件
步骤二:配置Redis加载模块
# 编辑redis.conf
vim /etc/redis/redis.conf
# 添加模块加载配置
loadmodule /path/to/redisbloom.so
loadmodule /path/to/rejson.so
loadmodule /path/to/redisearch.so
步骤三:重启并验证
# 重启Redis
systemctl restart redis
# 验证模块加载
redis-cli
127.0.0.1:6379> MODULE LIST
1) 1) "name"
2) "bf"
3) "ver"
4) (integer) 20612
5) "path"
6) "/path/to/redisbloom.so"
7) "args"
8) (empty array)
2) 1) "name"
2) "ReJSON"
3) "ver"
4) (integer) 20009
5) "path"
6) "/path/to/rejson.so"
7) "args"
8) (empty array)
常见问题排查
# 查看Redis日志
tail -f /var/log/redis/redis.log
# 权限问题(需要可执行权限)
chmod +x /path/to/redisbloom.so
五、Java客户端调用扩展模块
Spring Boot集成Redis Stack
1. 添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2. 布隆过滤器操作示例
@SpringBootTest
@RunWith(SpringRunner.class)
public class RedisStackTest {
@Resource
private RedisTemplate<String, Object> redisTemplate;
private final List<String> keys = List.of("user-bf");
/**
* 创建布隆过滤器
*/
@Test
public void createBloomFilter() {
if (!redisTemplate.hasKey("user-bf")) {
try {
String createFilterScriptText =
"return redis.call('BF.RESERVE', KEYS[1], '0.01', '1000', 'NONSCALING')";
DefaultRedisScript<String> redisScript =
new DefaultRedisScript<>(createFilterScriptText, String.class);
String result = redisTemplate.execute(redisScript, keys);
System.out.println("CREATE BF: " + result);
} catch (Exception e) {
System.out.println("COMMAND NOT SUPPORT: " + e.getMessage());
}
} else {
System.out.println("BF KEY already exists");
}
}
/**
* 添加数据到布隆过滤器
*/
@Test
public void addData() {
if (!redisTemplate.hasKey("user-bf")) {
System.out.println("BF KEY not exists");
return;
}
try {
String[] args = {"A", "B", "C", "D", "E", "F", "G"};
String addDataScriptText =
"for i, arg in ipairs(ARGV) do " +
" local addRes = redis.call('BF.ADD', KEYS[1], arg) " +
"end " +
"return 'OK'";
DefaultRedisScript<String> redisScript =
new DefaultRedisScript<>(addDataScriptText, String.class);
String result = redisTemplate.execute(redisScript, keys, args);
System.out.println("ADD DATA BF: " + result);
} catch (Exception e) {
System.out.println("COMMAND NOT SUPPORT: " + e.getMessage());
}
}
/**
* 检查数据是否存在
*/
@Test
public void checkData() {
if (!redisTemplate.hasKey("user-bf")) {
System.out.println("BF KEY not exists");
return;
}
String[] args = {"A", "B", "C"};
for (String arg : args) {
String checkDataScriptText =
"local checkRes = redis.call('BF.EXISTS', KEYS[1], ARGV[1]) " +
"return checkRes";
DefaultRedisScript<Long> redisScript =
new DefaultRedisScript<>(checkDataScriptText, Long.class);
try {
Long result = redisTemplate.execute(redisScript, keys, arg);
if (result == 1L) {
System.out.println("KEY " + arg + " EXISTS");
} else if (result == 0L) {
System.out.println("KEY " + arg + " NOT EXISTS");
} else {
System.out.println("ERROR for key: " + arg);
}
} catch (Exception e) {
System.out.println("COMMAND NOT SUPPORTED: " + e.getMessage());
}
}
}
}
3. Redis JSON操作示例
@Component
public class RedisJsonService {
@Resource
private RedisTemplate<String, Object> redisTemplate;
/**
* 设置JSON数据
*/
public void setJsonData(String key, Object value) {
String scriptText =
"return redis.call('JSON.SET', KEYS[1], '$', ARGV[1])";
DefaultRedisScript<String> script =
new DefaultRedisScript<>(scriptText, String.class);
String jsonStr = JSON.toJSONString(value);
redisTemplate.execute(script, List.of(key), jsonStr);
}
/**
* 获取JSON数据
*/
public <T> T getJsonData(String key, Class<T> clazz) {
String scriptText =
"return redis.call('JSON.GET', KEYS[1], '$')";
DefaultRedisScript<String> script =
new DefaultRedisScript<>(scriptText, String.class);
String jsonStr = redisTemplate.execute(script, List.of(key));
return JSON.parseObject(jsonStr, clazz);
}
}
六、最佳实践与性能优化
1. 扩展模块选择指南
| 使用场景 | 推荐模块 | 注意事项 |
|---|---|---|
| 存储复杂对象 | Redis JSON | 适合读多写少的场景 |
| 全文搜索 | RedisSearch | 数据量不超过内存限制 |
| 海量数据去重 | Bloom Filter | 接受一定误判率 |
| 需要删除的去重 | Cuckoo Filter | 空间占用略高于Bloom Filter |
2. 性能监控与调优
# 监控扩展模块性能
127.0.0.1:6379> INFO MODULES
# 监控内存使用
127.0.0.1:6379> INFO MEMORY
# 监控命令统计
127.0.0.1:6379> INFO COMMANDSTATS
3. 生产环境部署建议
-
版本兼容性:确保Redis版本与扩展模块版本匹配
-
内存规划:预留足够内存,特别是使用RedisSearch时
-
备份策略:定期备份扩展模块的数据结构
-
监控告警:设置关键指标的监控和告警
七、总结与展望
Redis Stack为Redis生态带来了革命性的增强,使得Redis从一个简单的缓存数据库,升级为功能丰富的多模型数据库。通过本文的介绍,我们可以看到:
Redis Stack的核心价值:
-
降低架构复杂度:无需额外引入MongoDB、Elasticsearch等中间件
-
提升开发效率:统一的API和开发范式
-
优化系统性能:内存级操作,极致性能体验
-
简化运维管理:单一技术栈,降低运维成本
未来发展趋势:
-
更多扩展模块:如图计算、时间序列等
-
云原生支持:更好的Kubernetes集成
-
AI集成:向量搜索、推荐算法等