Redis 核心功能全解析:功能描述、应用场景与 Java 实战
本文将全面覆盖 Redis 核心功能,从功能本质、应用场景到Java项目实战代码逐一解析,所有代码可直接复用,互相探讨
我将主要从数据结构、内存管理、持久化、分布式锁、主从复制、发布/订阅、哨兵、集群、布隆过滤器、性能优化、事务、Lua 脚本、Geo 地理信息 等全方位的介绍 Redis
Redis 核心功能
-
- [Redis 核心功能全解析:功能描述、应用场景与 Java 实战](#Redis 核心功能全解析:功能描述、应用场景与 Java 实战)
- 一、基础数据结构
-
- 1.字符串
- 2.哈希(Hash)
- 3.列表(List)
- 4.集合(Set)
- [5.有序集合(Sorted Set / Zset)](#5.有序集合(Sorted Set / Zset))
- 二、高级功能
-
- 1.内存管理
- 2.持久化
- 3.分布式锁
- 4.主从复制
- 5.发布订阅(Pub/Sub)
- 6.哨兵
- 7.集群(Cluster)
- [8.布隆过滤器(Bloom Filter)](#8.布隆过滤器(Bloom Filter))
- 9.事务(Transaction)
- 10.Lua脚本
- [11.Geo 地理信息](#11.Geo 地理信息)
- 12.HyperLogLog
- 13.Stream
一、基础数据结构
Redis 核心基础是5种原生数据结构,是所有高级功能的底层支撑,Java项目种通过Redis客户端直接操作。
1.字符串
Redis 最基础的数据类型,二进制安全(可存储文本、图片Base64等),支持字符串拼接、自增自减、过期时间设置等操作,value最大长度512MB。
应用场景
缓存单个值:如用户信息JSON串、商品详情
计数器:文章阅读量、接口调用次数
分布式限流:基于自增 + 过期时间
分布式锁的基础实现:setnx + expire
Java实战(Spring Data Redis)
java
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
@Component
public class RedisStringDemo {
@Resource
private StringRedisTemplate stringRedisTemplate;
// 1. 缓存用户信息(JSON格式)
public void cacheUserInfo(String userId, String userJson) {
// 设置key过期时间为1小时
stringRedisTemplate.opsForValue().set("user:info:" + userId, userJson, 1, TimeUnit.HOURS);
}
// 2. 计数器(文章阅读量自增)
public Long incrementArticleViewCount(String articleId) {
String key = "article:view:count:" + articleId;
// 自增1,不存在则默认从0开始
return stringRedisTemplate.opsForValue().increment(key, 1);
}
// 3. 分布式限流(单位时间内接口最大调用次数)
public boolean limitApiCall(String userId, String apiName, int maxCount, long period) {
String key = "limit:api:" + userId + ":" + apiName;
// 自增1,返回当前值
Long currentCount = stringRedisTemplate.opsForValue().increment(key, 1);
// 第一次调用时设置过期时间
if (currentCount != null && currentCount == 1) {
stringRedisTemplate.expire(key, period, TimeUnit.SECONDS);
}
// 超过最大次数则限流
return currentCount != null && currentCount <= maxCount;
}
}
2.哈希(Hash)
键值对集合(field-value),类似Java的HashMap,支持单个field增删改查,也可批量操作,适合存储结构化数据。
应用场景
存储对象类数据:如用户基本信息、商品属性
购物车:key为用户ID,field为商品ID,value为数量
配置项存储:如系统参数,field为参数名
Java实战(Spring Data Redis)
java
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Map;
@Component
public class RedisHashDemo {
@Resource
private StringRedisTemplate stringRedisTemplate;
// 1. 存储用户基本信息(结构化数据)
public void saveUserInfo(String userId, String username, String phone, String email) {
String key = "user:hash:" + userId;
// 批量设置field-value
stringRedisTemplate.opsForHash().putAll(key, Map.of(
"username", username,
"phone", phone,
"email", email
));
// 单独修改某个field
stringRedisTemplate.opsForHash().put(key, "phone", "13800138000");
}
// 2. 购物车操作(添加商品、修改数量、删除商品)
public void addCartItem(String userId, String productId, int quantity) {
String key = "cart:" + userId;
// 商品数量自增(不存在则默认0,自增后为quantity)
stringRedisTemplate.opsForHash().increment(key, productId, quantity);
}
public void removeCartItem(String userId, String productId) {
String key = "cart:" + userId;
stringRedisTemplate.opsForHash().delete(key, productId);
}
// 获取用户购物车所有商品
public Map<Object, Object> getUserCart(String userId) {
String key = "cart:" + userId;
return stringRedisTemplate.opsForHash().entries(key);
}
}
3.列表(List)
有序字符串列表(双向链表实现),支持从头部/尾部插入、删除元素,按索引访问,可实现栈(LIFO)、队列(FIFO)。
应用场景
消息队列:简单版,如异步通知、日志收集
最新消息列表:如朋友圈、系统公告
排行榜:基于时间顺序的最新榜单
Java实战(Spring Data Redis)
java
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
@Component
public class RedisListDemo {
@Resource
private StringRedisTemplate stringRedisTemplate;
// 1. 简单消息队列(生产者-消费者模式)
public void sendMessage(String queueName, String message) {
// 从列表尾部插入消息
stringRedisTemplate.opsForList().rightPush(queueName, message);
}
// 消费者(阻塞获取,避免空轮询)
public String consumeMessage(String queueName, long timeout) {
// 从列表头部获取消息,超时返回null
return stringRedisTemplate.opsForList().leftPop(queueName, timeout, java.util.concurrent.TimeUnit.SECONDS);
}
// 2. 最新公告列表(保留最近100条)
public void addAnnouncement(String announcement) {
String key = "announcement:latest";
// 尾部插入新公告
stringRedisTemplate.opsForList().rightPush(key, announcement);
// 限制列表长度为100,超出则删除头部旧数据
stringRedisTemplate.opsForList().trim(key, -100, -1);
}
// 获取最近10条公告
public List<String> getLatestAnnouncements(int count) {
String key = "announcement:latest";
// 从头部开始获取前count条(0到count-1)
return stringRedisTemplate.opsForList().range(key, 0, count - 1);
}
}
4.集合(Set)
无序、唯一的字符串集合,支持交集、并集、差集运算,底层是哈希表,查找、添加、删除复杂度O(1)。
应用场景
标签系统:如文章标签、商品分类
社交关系:用户关注列表、好友列表
去重操作:如日志去重、抽奖防重复参与
共同好友/兴趣推荐:基于交集运算
Java实战(Spring Data Redis)
java
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Set;
@Component
public class RedisSetDemo {
@Resource
private StringRedisTemplate stringRedisTemplate;
// 1. 标签系统(给文章添加标签、查询带某标签的文章)
public void addArticleTags(String articleId, String... tags) {
String key = "article:tags:" + articleId;
stringRedisTemplate.opsForSet().add(key, tags);
}
public Set<String> getArticlesByTag(String tag) {
// 假设key格式为"tag:articles:标签名",存储该标签下的所有文章ID
String key = "tag:articles:" + tag;
return stringRedisTemplate.opsForSet().members(key);
}
// 2. 社交关系(添加好友、获取共同好友)
public void addFriend(String userId, String friendId) {
String key = "user:friends:" + userId;
stringRedisTemplate.opsForSet().add(key, friendId);
// 双向好友,同时添加到对方列表
stringRedisTemplate.opsForSet().add("user:friends:" + friendId, userId);
}
public Set<String> getCommonFriends(String userId1, String userId2) {
String key1 = "user:friends:" + userId1;
String key2 = "user:friends:" + userId2;
// 计算两个集合的交集(共同好友)
return stringRedisTemplate.opsForSet().intersect(key1, key2);
}
// 3. 抽奖防重复(用户ID去重)
public boolean joinLottery(String lotteryId, String userId) {
String key = "lottery:participants:" + lotteryId;
// 添加成功返回true(未参与过),失败返回false(已参与)
return stringRedisTemplate.opsForSet().add(key, userId);
}
}
5.有序集合(Sorted Set / Zset)
有序、唯一的字符串集合,每个元素关联一个分数(score),按分数排序,支持按分数范围、排名范围查询。
应用场景
排行榜:如游戏积分、商品销量、文章点赞数
带权重的消息队列:按优先级处理
范围统计:如查询积分Top10用户、某分数段用户
Java实战(Spring Data Redis)
java
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Set;
@Component
public class RedisZSetDemo {
@Resource
private StringRedisTemplate stringRedisTemplate;
// 1. 商品销量排行榜(按销量降序)
public void incrementProductSales(String productId, long sales) {
String key = "rank:product:sales";
// 分数(销量)自增,不存在则默认0
stringRedisTemplate.opsForZSet().incrementScore(key, productId, sales);
}
// 获取销量TOP10商品(从高到低)
public Set<ZSetOperations.TypedTuple<String>> getSalesTop10() {
String key = "rank:product:sales";
// 0-9表示前10名,desc表示降序
return stringRedisTemplate.opsForZSet().reverseRangeWithScores(key, 0, 9);
}
// 2. 用户积分排行榜(查询用户排名和积分)
public Long getUserRank(String userId) {
String key = "rank:user:points";
// reverseRank返回降序排名(第1名返回0)
return stringRedisTemplate.opsForZSet().reverseRank(key, userId);
}
public Double getUserPoints(String userId) {
String key = "rank:user:points";
return stringRedisTemplate.opsForZSet().score(key, userId);
}
// 3. 范围查询(积分在100-500之间的用户)
public Set<String> getUsersByPointsRange(double min, double max) {
String key = "rank:user:points";
return stringRedisTemplate.opsForZSet().rangeByScore(key, min, max);
}
}
二、高级功能
1.内存管理
Redis 提供灵活的内存淘汰策略和过期时间机制,控制内存使用上限,避免内存溢出,同时自动清理过期数据。
内存上限配置:通过 maxmemory 设置最大可用内存
淘汰策略:如 allkeys-lru(淘汰最近最少使用的键)、volatile-lru(仅淘汰带过期时间的最近最少使用键)等
过期时间:支持给键设置 EX(秒)、PX(毫秒)过期,底层通过过期时间字典和惰性删除 + 定期删除机制实现
应用场景
缓存系统:自动清理过期缓存,避免缓存雪崩
临时数据存储:如验证码、临时令牌,自动过期无需手动删除
高并发场景下控制内存占用:避免缓存过多导致OOM
Java项目配置与使用
Redis 配置文件(redis.conf)
yaml
# 设置最大内存为4GB
maxmemory 4294967296
# 内存淘汰策略:淘汰最近最少使用的键(适用于缓存场景)
maxmemory-policy allkeys-lru
# 淘汰采样数量(默认5,值越大越精准但性能消耗略高)
maxmemory-samples 5
Java种设置过期时间(Spring Data Redis)
java
// 1. 给String类型设置过期时间(2分钟)
stringRedisTemplate.opsForValue().set("verify:code:13800138000", "666666", 2, TimeUnit.MINUTES);
// 2. 给Hash类型设置过期时间
String userKey = "user:hash:1001";
stringRedisTemplate.opsForHash().putAll(userKey, Map.of("username", "zhangsan"));
stringRedisTemplate.expire(userKey, 1, TimeUnit.HOURS);
// 3. 批量设置过期时间(通过Pipeline优化性能)
stringRedisTemplate.executePipelined((RedisCallback<Void>) connection -> {
for (int i = 1002; i <= 1010; i++) {
String key = "user:hash:" + i;
connection.hashCommands().hSet(key.getBytes(), "username".getBytes(), ("user" + i).getBytes());
connection.expire(key.getBytes(), 3600); // 1小时过期
}
return null;
});
2.持久化
Redis 支持两种持久化机制,将内存中的数据写入磁盘,避免服务重启后数据丢失
RDB(Redis Database):在指定时间间隔内生成数据集的快照(二进制文件),适合备份和大规模数据恢复
AOF(Append Only File):记录每一条写命令(文本格式),重启时重新执行命令恢复数据,数据一致性更高
应用场景
数据备份:如每日凌晨生成RDB快照,用于灾备
服务重启后数据恢复:生产环境常用 RDB+AOF混合模式
跨环境数据迁移:通过RDB文件导入导出数据
Java项目配置与使用
Redis 配置文件(redis.conf)
yaml
# ========== RDB配置 ==========
# 900秒内有1个键修改则触发快照
save 900 1
# 300秒内有10个键修改则触发快照
save 300 10
# 60秒内有10000个键修改则触发快照
save 60 10000
# 快照文件存储路径(默认当前目录)
dir ./
# 快照文件名(默认dump.rdb)
dbfilename dump.rdb
# ========== AOF配置 ==========
# 开启AOF(默认关闭,生产环境建议开启)
appendonly yes
# AOF文件名(默认appendonly.aof)
appendfilename "appendonly.aof"
# AOF同步策略:everysec(每秒同步,平衡性能和一致性)
appendfsync everysec
# 重写触发条件:当AOF文件大小是上次重写后大小的100%且大于64MB
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
Java中手动触发持久化(慎用,生产环境避免频繁调用)
java
// 手动触发RDB快照(同步,会阻塞Redis服务,建议用bgSave)
stringRedisTemplate.execute((RedisCallback<Void>) connection -> {
connection.serverCommands().save();
return null;
});
// 手动触发RDB快照(异步,不阻塞服务)
stringRedisTemplate.execute((RedisCallback<Void>) connection -> {
connection.serverCommands().bgSave();
return null;
});
// 手动触发AOF重写(异步)
stringRedisTemplate.execute((RedisCallback<Void>) connection -> {
connection.serverCommands().bgRewriteAof();
return null;
});
3.分布式锁
Redis 分布式锁是基于 Redis 原子操作实现的跨服务、跨进程互斥锁,用于在分布式系统中保证同一时刻只有一个线程 / 进程能访问共享资源,从而解决并发冲突(如超卖、数据错乱、重复执行等问题)。其设计核心依赖 Redis 的原子性、过期时间和唯一标识机制,是分布式系统中解决资源竞争的首选方案。
应用场景
秒杀 / 抢购系统:防止超卖
分布式任务调度:避免重复执行
共享资源更新:防止数据错乱
唯一标识生成:避免重复
长任务并发控制:防止中途锁释放
Java实战(Spring Data Redis)
java
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
@Component
public class RedisDistributedLockRedisson {
@Resource
private RedissonClient redissonClient;
/**
* 可重入锁(生产最常用)
* @param lockKey 锁Key
* @param waitTime 最大等待时间(秒):获取锁失败时,最多等待多久
* @param leaseTime 锁自动释放时间(秒):-1表示启用Watch Dog自动续约
* @return 加锁是否成功
*/
public boolean lock(String lockKey, long waitTime, long leaseTime) {
RLock lock = redissonClient.getLock(lockKey);
try {
// 尝试加锁:waitTime秒内获取锁,获取成功后leaseTime秒自动释放
return lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
/**
* 解锁方法
* @param lockKey 锁Key
*/
public void unlock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
// 仅释放当前线程持有的锁
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
/**
* 实战1:秒杀防超卖(短任务场景)
*/
public boolean seckill(String productId, String userId) {
String lockKey = "lock:seckill:" + productId;
try {
// 加锁:最多等待10秒,获取成功后5秒自动释放
boolean locked = lock(lockKey, 10, 5);
if (!locked) {
System.out.printf("用户%s秒杀商品%s失败:获取锁超时%n", userId, productId);
return false;
}
// 业务逻辑:查询库存→扣减库存→生成订单(同原生实现)
String stockKey = "product:stock:" + productId;
String stockStr = redissonClient.getString(stockKey);
if (stockStr == null || Integer.parseInt(stockStr) <= 0) {
System.out.printf("用户%s秒杀商品%s失败:库存不足%n", userId, productId);
return false;
}
redissonClient.getAtomicLong(stockKey).decrementAndGet();
System.out.printf("用户%s秒杀商品%s成功%n", userId, productId);
return true;
} finally {
// 解锁
unlock(lockKey);
}
}
/**
* 实战2:长任务处理(启用锁续约)
*/
public boolean processLargeFile(String fileId, String userId) {
String lockKey = "lock:file:process:" + fileId;
try {
// 加锁:最多等待10秒,leaseTime=-1(启用Watch Dog自动续约)
boolean locked = lock(lockKey, 10, -1);
if (!locked) {
System.out.printf("用户%s处理文件%s失败:未获取到锁%n", userId, fileId);
return false;
}
// 长任务执行(如文件上传、解析,模拟2分钟)
System.out.printf("用户%s开始处理文件%s...%n", userId, fileId);
Thread.sleep(120000);
System.out.printf("用户%s处理文件%s成功%n", userId, fileId);
return true;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
} finally {
// 解锁
unlock(lockKey);
}
}
}
4.主从复制
通过主从架构实现数据同步,主节点(Master)处理写请求,从节点(Slave)复制主节点数据并处理读请求,支持一主多从、多级从节点。
核心作用:读写分离(提升读吞吐量)、数据备份(从节点可作为备份节点)、故障转移(主节点故障后从节点可晋升为主)
同步机制:全量同步(从节点首次连接主节点)+ 增量同步(主节点后续写操作同步到从节点)
应用场景
高读并发场景:如电商商品详情查询、新闻列表浏览
数据备份与容灾:从节点部署在不同机房
避免单节点压力过大:写请求集中在主节点,读请求分散到从节点)
Java项目配置与使用
Redis主从配置(无需修改Java代码,通过Redis配置实现)
主节点配置(redis-master.conf):默认无需特殊配置,只需开启端口(6379)并允许从节点连接
从节点配置(redis-slave-conf):
yaml
# 从节点端口(避免与主节点冲突)
port 6380
# 配置主节点地址和端口
replicaof 192.168.1.100 6379
# 主节点有密码时配置
masterauth "redis123456"
# 从节点只读(默认开启,避免从节点处理写请求)
replica-read-only yes
Java中读写分离实现(Spring Data Rdis 配置)
java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import redis.clients.jedis.JedisPoolConfig;
@Configuration
public class RedisMasterSlaveConfig {
// 主节点连接工厂(写操作)
@Bean("masterRedisConnectionFactory")
public RedisConnectionFactory masterConnectionFactory() {
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
config.setHostName("192.168.1.100");
config.setPort(6379);
config.setPassword("redis123456");
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(100);
poolConfig.setMaxIdle(20);
poolConfig.setMinIdle(5);
return new JedisConnectionFactory(config, poolConfig);
}
// 从节点连接工厂(读操作)
@Bean("slaveRedisConnectionFactory")
public RedisConnectionFactory slaveConnectionFactory() {
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
config.setHostName("192.168.1.101");
config.setPort(6380);
config.setPassword("redis123456");
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(200);
poolConfig.setMaxIdle(50);
poolConfig.setMinIdle(10);
return new JedisConnectionFactory(config, poolConfig);
}
// 写操作Template(关联主节点)
@Bean("writeRedisTemplate")
public StringRedisTemplate writeRedisTemplate(@org.springframework.beans.factory.annotation.Qualifier("masterRedisConnectionFactory") RedisConnectionFactory factory) {
return new StringRedisTemplate(factory);
}
// 读操作Template(关联从节点)
@Bean("readRedisTemplate")
public StringRedisTemplate readRedisTemplate(@org.springframework.beans.factory.annotation.Qualifier("slaveRedisConnectionFactory") RedisConnectionFactory factory) {
return new StringRedisTemplate(factory);
}
}
Java中使用读写分离
java
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Component
public class RedisMasterSlaveDemo {
// 写操作(主节点)
@Resource(name = "writeRedisTemplate")
private StringRedisTemplate writeRedisTemplate;
// 读操作(从节点)
@Resource(name = "readRedisTemplate")
private StringRedisTemplate readRedisTemplate;
// 写操作(如更新商品库存)
public void updateProductStock(String productId, int stock) {
String key = "product:stock:" + productId;
writeRedisTemplate.opsForValue().set(key, String.valueOf(stock));
}
// 读操作(如查询商品库存)
public String getProductStock(String productId) {
String key = "product:stock:" + productId;
return readRedisTemplate.opsForValue().get(key);
}
}
5.发布订阅(Pub/Sub)
Redis原生支持发布订阅模式,生产者(Publisher)发布消息到频道(Channel),消费者(Subscriber)订阅频道并接收消息,支持多订阅者、多频道
应用场景
实时通知:如订单状态变更通知、系统公告推送
事件触发:如用户注册成功后触发积分赠送、短信发送
Java实战(Spring Data Redis)
java
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
@Component
public class RedisPubSubDemo {
@Resource
private StringRedisTemplate stringRedisTemplate;
@Resource
private RedisMessageListenerContainer redisMessageListenerContainer;
// 1. 生产者:发布消息到频道
public void publishMessage(String channel, String message) {
stringRedisTemplate.convertAndSend(channel, message);
}
// 2. 消费者:订阅频道(监听订单状态变更)
@PostConstruct
public void subscribeOrderChannel() {
String channel = "channel:order:status";
// 注册监听器
redisMessageListenerContainer.addMessageListener(new MessageListener() {
@Override
public void onMessage(Message message, byte[] pattern) {
// 接收消息并处理
String messageBody = new String(message.getBody());
String channelName = new String(message.getChannel());
System.out.println("从频道 " + channelName + " 接收消息:" + messageBody);
// 业务处理逻辑(如推送通知给用户)
handleOrderStatusChange(messageBody);
}
}, new ChannelTopic(channel));
}
// 处理订单状态变更消息
private void handleOrderStatusChange(String message) {
// 解析消息(假设消息为JSON格式)
System.out.println("处理订单状态变更:" + message);
}
// 示例:发布订单支付成功消息
public void publishOrderPaidMessage(String orderId) {
String message = "{\"orderId\":\"" + orderId + "\",\"status\":\"PAID\",\"time\":\"2024-01-01 12:00:00\"}";
publishMessage("channel:order:status", message);
}
}
6.哨兵
Redis 哨兵是主从架构的高可用解决方案,由多个哨兵节点组成集群,负责监控主从节点状态:
故障检测:实时监控主节点是否存活,若主节点宕机,自动触发故障转移
自动故障转移:将某个从节点晋升为主节点,其他从节点切换到新主节点同步数据
配置管理:客户端通过哨兵获取主节点地址,无需硬编码主节点IP
应用场景
生产环境主从架构的高可用保障:避免主节点故障导致服务不可用
无人值守的故障恢复:无需人工干预切换主从
Java项目配置与使用
哨兵配置文件(sentinel.conf)
yaml
# 哨兵节点端口(每个哨兵节点端口不同,如26379、26380、26381)
port 26379
# 监控主节点:名称(自定义)、主节点IP:端口、故障判定所需的哨兵节点数量(quorum)
sentinel monitor mymaster 192.168.1.100 6379 2
# 主节点密码(若有)
sentinel auth-pass mymaster redis123456
# 主节点无响应超时时间(默认30秒,单位毫秒)
sentinel down-after-milliseconds mymaster 30000
# 故障转移超时时间(默认180秒)
sentinel failover-timeout mymaster 180000
Java中通过哨兵连接Redis(Spring Data Redis 配置)
java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import redis.clients.jedis.JedisPoolConfig;
import java.util.HashSet;
import java.util.Set;
@Configuration
public class RedisSentinelConfig {
@Bean
public RedisSentinelConfiguration sentinelConfiguration() {
RedisSentinelConfiguration config = new RedisSentinelConfiguration();
// 主节点名称(需与哨兵配置一致)
config.master("mymaster");
// 哨兵节点集合
Set<String> sentinelNodes = new HashSet<>();
sentinelNodes.add("192.168.1.100:26379");
sentinelNodes.add("192.168.1.101:26380");
sentinelNodes.add("192.168.1.102:26381");
config.setSentinels(sentinelNodes);
// Redis密码
config.setPassword("redis123456");
return config;
}
@Bean
public JedisConnectionFactory jedisConnectionFactory(RedisSentinelConfiguration sentinelConfig) {
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(200);
poolConfig.setMaxIdle(50);
poolConfig.setMinIdle(10);
return new JedisConnectionFactory(sentinelConfig, poolConfig);
}
@Bean
public StringRedisTemplate stringRedisTemplate(JedisConnectionFactory factory) {
return new StringRedisTemplate(factory);
}
}
Java中使用(与普通Redis使用方式一致,哨兵自动处理故障转移)
java
// 无需修改业务代码,直接使用StringRedisTemplate即可
@Component
public class RedisSentinelDemo {
@Resource
private StringRedisTemplate stringRedisTemplate;
public void setValue(String key, String value) {
stringRedisTemplate.opsForValue().set(key, value, 1, java.util.concurrent.TimeUnit.HOURS);
}
public String getValue(String key) {
return stringRedisTemplate.opsForValue().get(key);
}
}
7.集群(Cluster)
Redis Cluster 是分布式解决方案,将数据分片存储在多个节点(最少3主3从),每个主节点负责一部分哈希槽(共16384个),支持水平扩容和高可用。
核心特性:数据分片(按key的哈希值分配到不同节点)、自动故障转移(主节点故障后从节点晋升)、水平扩容(新增节点后重新分配哈希槽)
优势:解决单节点内存上限问题,支持大规模数据存储和高并发访问
应用场景
大规模数据存储:如千万级别用户的会话存储、亿级商品缓存
超高并发场景:如电商秒杀、双11峰值流量
需水平扩容的业务:避免单节点硬件瓶颈
Java项目配置与使用
Redis Cluster集群搭建
每个节点配置 cluster-enabled yes(开启集群模式)
每个节点配置 cluster-config-file nodes.conf(集群节点信息文件)
通过 redis-cli --cluster create 命令创建集群(如3主3从)
bash
redis-cli --cluster create 192.168.1.100:6379 192.168.1.101:6379 192.168.1.102:6379 192.168.1.103:6380 192.168.1.104:6380 192.168.1.105:6380 --cluster-replicas 1 -a redis123456
Java中连接Redis Cluster(Spring Data Redis 配置)
java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import redis.clients.jedis.JedisPoolConfig;
import java.util.Arrays;
import java.util.List;
@Configuration
public class RedisClusterConfig {
@Bean
public RedisClusterConfiguration clusterConfiguration() {
RedisClusterConfiguration config = new RedisClusterConfiguration();
// 集群节点列表(只需配置主节点,从节点会自动发现)
List<String> clusterNodes = Arrays.asList(
"192.168.1.100:6379",
"192.168.1.101:6379",
"192.168.1.102:6379"
);
config.setClusterNodes(clusterNodes);
// 最大重定向次数(默认3)
config.setMaxRedirects(3);
// Redis密码
config.setPassword("redis123456");
return config;
}
@Bean
public JedisConnectionFactory jedisConnectionFactory(RedisClusterConfiguration clusterConfig) {
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(300);
poolConfig.setMaxIdle(100);
poolConfig.setMinIdle(20);
// 开启集群模式支持
return new JedisConnectionFactory(clusterConfig, poolConfig);
}
@Bean
public StringRedisTemplate stringRedisTemplate(JedisConnectionFactory factory) {
return new StringRedisTemplate(factory);
}
}
Java中使用 Redis Cluster(支持所有基础操作,自动处理分片)
java
@Component
public class RedisClusterDemo {
@Resource
private StringRedisTemplate stringRedisTemplate;
// 存储用户会话(自动分片到对应节点)
public void saveUserSession(String sessionId, String userInfo) {
String key = "session:" + sessionId;
stringRedisTemplate.opsForValue().set(key, userInfo, 2, java.util.concurrent.TimeUnit.HOURS);
}
// 获取用户会话(自动路由到存储该key的节点)
public String getUserSession(String sessionId) {
String key = "session:" + sessionId;
return stringRedisTemplate.opsForValue().get(key);
}
// ZSet操作(排行榜,自动分片)
public void incrementRank(String rankKey, String member, double score) {
stringRedisTemplate.opsForZSet().incrementScore(rankKey, member, score);
}
}
8.布隆过滤器(Bloom Filter)
Redis 4.0+ 支持布隆过滤器插件(redisbloom),是一种空间高效的概率性数据结构,用于判断一个元素是否在集合中。
核心特性:查询效率高(O(k),k为哈希函数个数)、空间占用小,存在误判率(不会漏判,可能把不存在的元素判定为存在)
原理:通过多个哈希函数将元素映射到位图的多个位置,标记为1;查询时若所有映射位置都是1,则可能存在,否则一定不存在
应用场景
缓存穿透防护:过滤不存在的key,避免请求穿透到数据库)
大数据量去重:如日志去重、爬虫URL去重
黑名单校验:如垃圾邮件地址、恶意IP黑名单
Java项目配置与使用
Redis 布隆过滤器安装(需先安装插件)
bash
# 下载redisbloom插件
git clone https://github.com/RedisBloom/RedisBloom.git
cd RedisBloom
make
# 启动Redis时加载插件
redis-server --loadmodule /path/to/redisbloom.so
Java中使用布隆过滤器(基于 Jedis客户端,Spring Data Redis 需扩展)
java
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import javax.annotation.Resource;
@Component
public class RedisBloomFilterDemo {
@Resource
private JedisPool jedisPool;
// 布隆过滤器初始化(指定key、预计元素个数、误判率)
public void initBloomFilter(String filterKey, long expectedInsertions, double falseProbability) {
try (Jedis jedis = jedisPool.getResource()) {
// BF.RESERVE 命令:创建布隆过滤器
jedis.executeCommand("BF.RESERVE", filterKey, String.valueOf(falseProbability), String.valueOf(expectedInsertions));
}
}
// 添加元素到布隆过滤器
public void addToBloomFilter(String filterKey, String element) {
try (Jedis jedis = jedisPool.getResource()) {
// BF.ADD 命令:添加单个元素
jedis.executeCommand("BF.ADD", filterKey, element);
// 批量添加:BF.MADD filterKey elem1 elem2 elem3
// jedis.executeCommand("BF.MADD", filterKey, "elem1", "elem2", "elem3");
}
}
// 检查元素是否在布隆过滤器中
public boolean existsInBloomFilter(String filterKey, String element) {
try (Jedis jedis = jedisPool.getResource()) {
// BF.EXISTS 命令:返回1表示可能存在,0表示一定不存在
Long result = (Long) jedis.executeCommand("BF.EXISTS", filterKey, element);
return result != null && result == 1;
}
}
// 缓存穿透防护示例(查询商品前先检查布隆过滤器)
public String getProductById(String productId) {
String cacheKey = "product:info:" + productId;
String filterKey = "bloom:filter:product";
// 1. 先检查布隆过滤器,不存在则直接返回null(避免穿透到DB)
if (!existsInBloomFilter(filterKey, productId)) {
return null;
}
// 2. 布隆过滤器认为存在,查询Redis缓存
String productInfo = stringRedisTemplate.opsForValue().get(cacheKey);
if (productInfo != null) {
return productInfo;
}
// 3. 缓存未命中,查询数据库(此处省略DB查询逻辑)
String dbProductInfo = "从DB查询到的商品信息";
// 4. 存入Redis缓存
stringRedisTemplate.opsForValue().set(cacheKey, dbProductInfo, 1, java.util.concurrent.TimeUnit.HOURS);
return dbProductInfo;
}
}
9.事务(Transaction)
Redis事务是一组命令的集合,支持 MULTI(开启事务)、EXEC(执行事务)、DISCARD(取消事务)、WATCH(乐观锁)命令:
核心特性:批量执行命令(要么全部执行,要么全部不执行,无部分执行),但不支持回滚(Redis事务是"弱事务) 乐观锁:通过 WATCH
监控键,若事务执行前键被修改,则事务取消执行
应用场景
原子性操作组合:如扣减库存 + 增加订单数
避免并发修改冲突:如秒杀场景下的库存扣减
Java实战(Spring Data Redis)
java
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
@Component
public class RedisTransactionDemo {
@Resource
private StringRedisTemplate stringRedisTemplate;
// 原子操作:扣减库存+增加销量(无乐观锁,适合低并发场景)
public void deductStockAndAddSales(String productId, int num) {
stringRedisTemplate.execute(session -> {
// 开启事务
session.multi();
// 1. 扣减库存(假设库存key为product:stock:xxx)
String stockKey = "product:stock:" + productId;
session.opsForValue().decrement(stockKey, num);
// 2. 增加销量(假设销量key为product:sales:xxx)
String salesKey = "product:sales:" + productId;
session.opsForValue().increment(salesKey, num);
// 执行事务,返回结果列表
List<Object> results = session.exec();
// results.get(0) 是decrement的结果,results.get(1) 是increment的结果
return results;
});
}
// 乐观锁实现:秒杀场景下的库存扣减(避免超卖)
public boolean seckillWithOptimisticLock(String productId, String userId, int num) {
String stockKey = "product:stock:" + productId;
String orderKey = "seckill:order:" + productId + ":" + userId;
// 使用RedisCallback执行底层命令,支持WATCH
return stringRedisTemplate.execute((RedisCallback<Boolean>) connection -> {
// 1. 监控库存键(乐观锁)
connection.watch(stockKey.getBytes());
// 2. 查询当前库存
byte[] stockBytes = connection.get(stockKey.getBytes());
if (stockBytes == null) {
connection.unwatch();
return false; // 商品不存在
}
int currentStock = Integer.parseInt(new String(stockBytes));
if (currentStock < num) {
connection.unwatch();
return false; // 库存不足
}
// 3. 开启事务
connection.multi();
// 4. 扣减库存
connection.decrBy(stockKey.getBytes(), num);
// 5. 记录订单(标记用户已购买)
connection.set(orderKey.getBytes(), "1".getBytes());
// 6. 执行事务(若库存被其他线程修改,exec返回null)
List<Object> results = connection.exec();
// 7. 事务执行成功则返回true,否则返回false
return results != null && !results.isEmpty();
});
}
}
10.Lua脚本
Redis支持嵌入 Lua 脚本执行,脚本中的命令会被原子化执行(中途不会被其他命令打断),支持调用Redis命令API,可实现复杂的原子逻辑
核心优势:原子性(避免多命令并发冲突)、减少网络开销(多命令合并为一个脚本调用)、灵活扩展(实现Redis原生不支持的复杂逻辑)
应用场景
复杂原子操作:如秒杀库存扣减 + 订单创建 + 日志记录
自定义数据结构操作:如基于ZSet实现带过期时间的排行榜
分布式锁的高级实现:如带过期时间的互斥锁
Java实战(Spring Data Redis)
java
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Component
public class RedisLuaScriptDemo {
@Resource
private StringRedisTemplate stringRedisTemplate;
// 1. 秒杀场景:原子执行"库存检查+扣减+订单创建"
public boolean seckillWithLua(String productId, String userId, int num) {
// Lua脚本:先检查库存,库存充足则扣减并记录订单,返回1;否则返回0
String luaScript = """
local stockKey = KEYS[1]
local orderKey = KEYS[2]
local num = tonumber(ARGV[1])
local userId = ARGV[2]
-- 检查用户是否已购买(防重复下单)
local hasOrdered = redis.call('get', orderKey .. ':' .. userId)
if hasOrdered then
return 0
end
-- 检查库存
local currentStock = tonumber(redis.call('get', stockKey))
if not currentStock or currentStock < num then
return 0
end
-- 扣减库存
redis.call('decrby', stockKey, num)
-- 记录订单(设置1小时过期)
redis.call('setex', orderKey .. ':' .. userId, 3600, '1')
return 1
""";
// 执行Lua脚本:KEYS为键列表,ARGV为参数列表
Long result = stringRedisTemplate.execute(
new org.springframework.data.redis.core.script.DefaultRedisScript<>(luaScript, Long.class),
java.util.List.of("product:stock:" + productId, "seckill:order:" + productId), // KEYS
String.valueOf(num), userId // ARGV
);
return result != null && result == 1;
}
// 2. 分布式锁:基于Lua脚本实现"加锁+过期时间"原子操作
public boolean acquireLock(String lockKey, String lockValue, long expireTime) {
// Lua脚本:如果锁不存在则设置锁和过期时间,返回1;否则返回0
String luaScript = """
local lockKey = KEYS[1]
local lockValue = ARGV[1]
local expireTime = tonumber(ARGV[2])
if redis.call('setnx', lockKey, lockValue) == 1 then
redis.call('expire', lockKey, expireTime)
return 1
end
return 0
""";
Long result = stringRedisTemplate.execute(
new org.springframework.data.redis.core.script.DefaultRedisScript<>(luaScript, Long.class),
java.util.List.of(lockKey),
lockValue, String.valueOf(expireTime)
);
return result != null && result == 1;
}
// 3. 分布式锁:基于Lua脚本实现"解锁(仅删除自己的锁)"原子操作
public boolean releaseLock(String lockKey, String lockValue) {
// Lua脚本:只有锁存在且值匹配时才删除,避免误删其他线程的锁
String luaScript = """
local lockKey = KEYS[1]
local lockValue = ARGV[1]
if redis.call('get', lockKey) == lockValue then
redis.call('del', lockKey)
return 1
end
return 0
""";
Long result = stringRedisTemplate.execute(
new org.springframework.data.redis.core.script.DefaultRedisScript<>(luaScript, Long.class),
java.util.List.of(lockKey),
lockValue
);
return result != null && result == 1;
}
}
11.Geo 地理信息
Redis 3.2+ 原生支持Geo数据类型,用于存储地理位置信息(经纬度),支持距离计算、范围查询、附近的人等操作
核心命令:GEOADD(添加地理位置)、GEODIST(计算两点距离)、GEORADIUS(根据坐标查询范围内的点)、GEORADIUSBYMEMBER(根据成员查询范围内的点)
应用场景
附近的人/商家:如外卖商家推荐、社交软件附近好友
地理位置距离计算:如两地距离、配送范围判断
区域筛选:如查询某城市范围内的景点
Java实战(Spring Data Redis)
java
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.GeoOperations;
import org.springframework.data.redis.domain.geo.GeoDistance;
import org.springframework.data.redis.domain.geo.GeoResults;
import org.springframework.data.redis.domain.geo.Metric;
import org.springframework.data.redis.domain.geo.Point;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Component
public class RedisGeoDemo {
@Resource
private StringRedisTemplate stringRedisTemplate;
// 1. 添加地理位置(如外卖商家)
public void addMerchantLocation(String merchantId, double longitude, double latitude) {
GeoOperations<String, String> geoOps = stringRedisTemplate.opsForGeo();
// GEOADD key 经度 纬度 成员
geoOps.add("geo:merchant", new Point(longitude, latitude), merchantId);
}
// 2. 计算两个商家之间的距离(单位:千米)
public double calculateMerchantDistance(String merchantId1, String merchantId2) {
GeoOperations<String, String> geoOps = stringRedisTemplate.opsForGeo();
// GEODIST key 成员1 成员2 单位(km/m/mi/ft)
GeoDistance distance = geoOps.distance("geo:merchant", merchantId1, merchantId2, Metric.KILOMETERS);
return distance != null ? distance.getValue() : 0;
}
// 3. 附近的商家查询(根据用户坐标,查询5公里内的商家,按距离排序)
public GeoResults<org.springframework.data.redis.domain.geo.GeoLocation<String>> findNearbyMerchants(double userLongitude, double userLatitude, double radiusKm) {
GeoOperations<String, String> geoOps = stringRedisTemplate.opsForGeo();
// GEORADIUS key 经度 纬度 半径 单位 WITHDIST WITHLATLNG ASC
return geoOps.radius(
"geo:merchant",
new Point(userLongitude, userLatitude),
new Distance(radiusKm, Metric.KILOMETERS),
org.springframework.data.redis.domain.geo.GeoRadiusCommandArgs.newGeoRadiusArgs()
.includeDistance() // 包含距离
.includeCoordinates() // 包含经纬度
.sortAscending() // 按距离升序排序
);
}
// 4. 根据商家查询附近的用户(如配送员查找附近下单用户)
public GeoResults<org.springframework.data.redis.domain.geo.GeoLocation<String>> findUsersNearMerchant(String merchantId, double radiusKm) {
GeoOperations<String, String> geoOps = stringRedisTemplate.opsForGeo();
// GEORADIUSBYMEMBER key 成员 半径 单位 WITHDIST
return geoOps.radius(
"geo:user",
merchantId,
new Distance(radiusKm, Metric.KILOMETERS),
org.springframework.data.redis.domain.geo.GeoRadiusCommandArgs.newGeoRadiusArgs()
.includeDistance()
.sortAscending()
);
}
}
12.HyperLogLog
Redis原生支持的概率性数据结构,用于高效统计集合的基数(不重复元素个数),占用内存极小(无论数据量多大,仅需约12KB)
核心特性:内存高效(适合超大数据量技术统计),存在极小误差(约0.81%),不存储具体元素,仅统计基数
应用场景
网站UV统计:每日访问独立用户数
接口调用独立IP统计
商品被浏览的独立用户数统计
Java实战(Spring Data Redis)
java
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Component
public class RedisHyperLogLogDemo {
@Resource
private StringRedisTemplate stringRedisTemplate;
// 1. 统计网站UV(每日独立用户)
public void recordWebsiteUV(String date, String userId) {
String key = "uv:website:" + date;
// PFADD 命令:添加用户ID到HyperLogLog
stringRedisTemplate.opsForHyperLogLog().add(key, userId);
}
// 2. 获取每日UV数量
public long getWebsiteUV(String date) {
String key = "uv:website:" + date;
// PFCOUNT 命令:统计基数(独立用户数)
return stringRedisTemplate.opsForHyperLogLog().size(key);
}
// 3. 合并多个日期的UV(如统计本周UV)
public long mergeWeeklyUV(String weekKey, String... dailyKeys) {
// PFMERGE 命令:合并多个HyperLogLog到一个key
stringRedisTemplate.opsForHyperLogLog().union(weekKey, dailyKeys);
// 统计合并后的基数
return stringRedisTemplate.opsForHyperLogLog().size(weekKey);
}
// 4. 接口独立IP统计
public void recordApiIp(String apiName, String ip) {
String key = "uv:api:" + apiName;
stringRedisTemplate.opsForHyperLogLog().add(key, ip);
}
public long getApiUniqueIpCount(String apiName) {
String key = "uv:api:" + apiName;
return stringRedisTemplate.opsForHyperLogLog().size(key);
}
}
13.Stream
Redis 5.0+ 引入的高性能消息队列数据结构,支持持久化、消费组、消息确认、回溯消费等特性,功能媲美专业消息队列(如RabbitMQ)
核心特性:消息持久化(基于AOF/RDB)、消费组模式(支持多消费者负载均衡)、消息ID自增(保证顺序)、消息确认(ACK)机制
应用场景
高可靠消息队列:如订单创建、支付回调通知
日志收集:按时间顺序存储日志,支持回溯查询
异步任务处理:如订单超时取消、短信发送
Java实战(基于 Jedis 客户端)
java
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.StreamEntry;
import redis.clients.jedis.StreamEntryID;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Component
public class RedisStreamDemo {
@Resource
private JedisPool jedisPool;
// 1. 生产者:发送消息到Stream
public String sendMessage(String streamKey, Map<String, String> message) {
try (Jedis jedis = jedisPool.getResource()) {
// XADD 命令:添加消息到Stream,自动生成消息ID(如1699999999999-0)
StreamEntryID entryId = jedis.xadd(streamKey, null, message);
return entryId.toString();
}
}
// 2. 消费者组:创建消费组(首次使用时创建)
public void createConsumerGroup(String streamKey, String groupName) {
try (Jedis jedis = jedisPool.getResource()) {
// XGROUP CREATE 命令:创建消费组,从最新消息开始消费($表示最新)
jedis.xgroupCreate(streamKey, groupName, new StreamEntryID("$"), true);
}
}
// 3. 消费者:从消费组获取消息并确认
public void consumeMessageFromGroup(String streamKey, String groupName, String consumerName) {
try (Jedis jedis = jedisPool.getResource()) {
while (true) {
// XREADGROUP 命令:从消费组获取消息,阻塞10秒(0表示无限阻塞)
Map<String, List<StreamEntry>> messages = jedis.xreadGroup(
groupName, consumerName,
1, // 每次获取1条消息
10000, // 阻塞10秒
false, // 不重复获取已确认的消息
Map.of(streamKey, StreamEntryID.UNRECEIVED_ENTRY) // 获取未消费的消息
);
if (messages == null || messages.isEmpty()) {
continue;
}
// 处理消息
List<StreamEntry> streamEntries = messages.get(streamKey);
for (StreamEntry entry : streamEntries) {
String messageId = entry.getID().toString();
Map<String, String> message = entry.getFields();
// 业务处理逻辑(如处理订单消息)
System.out.println("处理消息:" + messageId + ",内容:" + message);
// 确认消息(ACK):XACK 命令
jedis.xack(streamKey, groupName, entry.getID());
}
}
}
}
// 4. 普通消费者:非消费组模式(适合单消费者)
public void consumeMessage(String streamKey) {
try (Jedis jedis = jedisPool.getResource()) {
StreamEntryID lastId = StreamEntryID.LAST_ENTRY; // 从最后一条消息开始消费
while (true) {
Map<String, List<StreamEntry>> messages = jedis.xread(
1,
10000,
false,
Map.of(streamKey, lastId)
);
if (messages == null || messages.isEmpty()) {
continue;
}
List<StreamEntry> streamEntries = messages.get(streamKey);
for (StreamEntry entry : streamEntries) {
System.out.println("普通消费者处理消息:" + entry.getFields());
lastId = entry.getID(); // 更新最后消费的消息ID
}
}
}
}
// 示例:发送订单消息
public void sendOrderMessage(String orderId, String userId, String amount) {
Map<String, String> message = new HashMap<>();
message.put("orderId", orderId);
message.put("userId", userId);
message.put("amount", amount);
message.put("status", "PENDING");
sendMessage("stream:order", message);
}
}