小熊学Java:https://javaxiaobear.cn
【2024年】编程资料大合集,年度精选资源一键收藏
今天来聊聊Redis的那些事儿,你可能不知道,Redis在我们的日常开发中有着超多的应用场景,下面我就来给大家逐一揭秘!
1. 缓存
Redis 使用 String
类型存储热点数据。
例如:热点数据缓存(例如报表、明星出轨),对象缓存、全页缓存、可以提升热点数据的访问数据。
命令:
SET hot_report:reportId 数据内容 EX 3600
GET hot_report:reportId
Java 实现:
/**
* 缓存热点数据(例如报表、明星新闻等)
*
* @param reportId 缓存的唯一标识(报表 ID)
* @return 热点数据(从缓存或数据库加载)
*/
public String getHotData(String reportId) {
String key = "hot_report:" + reportId; // 构造 Redis 缓存的 Key
String data = redisUtil.get(key); // 从 Redis 获取缓存数据
if (data == null) { // 如果 Redis 缓存不存在,则从数据库查询
data = "数据内容"; // 模拟数据库查询
redisUtil.set(key, data, 3600); // 将数据存入 Redis,设置过期时间为 3600 秒
}
return data;
}
2. 分布式数据共享
String 类型,因为 Redis 是分布式的独立服务,可以在多个应用之间共享,使用 Redis 实现分布式 Session。
命令:
SET session:sessionId userInfo EX 3600
GET session:sessionId
Java 实现:
/**
* 保存分布式 Session 数据
*
* @param sessionId 用户的 Session ID
* @param userInfo 用户相关信息
*/
public void saveSession(String sessionId, String userInfo) {
String key = "session:" + sessionId; // 构造 Redis Key
redisUtil.set(key, userInfo, 3600); // 存入 Redis,过期时间为 1 小时
}
/**
* 获取分布式 Session 数据
*
* @param sessionId 用户的 Session ID
* @return 用户相关信息
*/
public String getSession(String sessionId) {
String key = "session:" + sessionId; // 构造 Redis Key
return redisUtil.get(key); // 从 Redis 获取用户数据
}
3. 分布式锁
Redis 利用 SETNX
和 EXPIRE
实现分布式锁。利用Redis的SETNX命令和过期时间,可以实现分布式锁。保证多个进程或线程在访问共享资源时的互斥性,防止数据冲突。
命令:
SETNX lock:key 1
EXPIRE lock:key 10
DEL lock:key
Java 实现:
/**
* 获取分布式锁
*
* @param key 锁的 Key
* @param value 锁的唯一标识(防止误删)
* @param timeout 锁的过期时间(秒)
* @return 是否成功获取锁
*/
public boolean lock(String key, String value, long timeout) {
Boolean result = redisTemplate.opsForValue().setIfAbsent(key, value, timeout, TimeUnit.SECONDS); // SETNX + EXPIRE
return result != null && result; // 返回是否成功加锁
}
/**
* 释放分布式锁
*
* @param key 锁的 Key
* @param value 锁的唯一标识(防止误删)
*/
public void unlock(String key, String value) {
String currentValue = redisUtil.get(key); // 获取当前锁的值
if (value.equals(currentValue)) { // 确认锁是当前线程持有
redisUtil.delete(key); // 删除锁
}
}
4. 全局 ID
Redis 利用 INCRBY
方法生成全局唯一 ID。Redis可以生成全局唯一的ID。通过原子操作,如INCR命令,可以生成自增ID,适用于订单号、用户ID等需要唯一标识的场景。
命令:
INCRBY userid 1000
Java 实现:
/**
* 获取全局唯一 ID
*
* @param key Redis 中自增的 Key
* @return 自增的唯一 ID
*/
public long getNextId(String key) {
return redisUtil.increment(key, 1); // 每次调用自增 1
}
5. 计数器
int类型,incr方法
Redis 使用 INCR
实现计数功能,例如统计阅读量、点赞数。
例如:文章的阅读量、微博点赞数、允许一定的延迟,先写入Redis再定时同步到数据库
命令:
INCR article:view:articleId
GET article:view:articleId
Java 实现:
/**
* 增加文章阅读量
*
* @param articleId 文章的唯一 ID
*/
public void increaseViewCount(String articleId) {
String key = "article:view:" + articleId; // 构造 Redis Key
redisUtil.increment(key, 1); // 阅读量自增 1
}
/**
* 获取文章阅读量
*
* @param articleId 文章的唯一 ID
* @return 阅读量
*/
public long getViewCount(String articleId) {
String key = "article:view:" + articleId; // 构造 Redis Key
return Long.parseLong(redisUtil.get(key)); // 从 Redis 获取阅读量
}
6. 限流
Redis 使用 INCR
和 EXPIRE
限制单位时间内的操作次数。
以访问者的ip和其他信息作为key,访问一次增加一次计数,超过次数则返回false
利用Redis的固定窗口、滑动窗口等算法,可以实现限流功能。防止系统被过多请求压垮,如限制每个用户每秒只能访问某个接口10次。
命令:
INCR rate_limit:userId:action
EXPIRE rate_limit:userId:action 60
Java 实现:
/**
* 判断用户是否被限流(每分钟限制 5 次)
*
* @param userId 用户 ID
* @param action 限流的操作
* @return 是否允许操作
*/
public boolean isAllowed(String userId, String action) {
String key = "rate_limit:" + userId + ":" + action; // 构造 Redis Key
long count = redisUtil.increment(key, 1); // 自增计数
if (count == 1) {
redisTemplate.expire(key, 60, TimeUnit.SECONDS); // 如果是第一次操作,设置过期时间为 60 秒
}
return count <= 5; // 超过 5 次操作则限流
}
7. 在线用户统计
Redis 使用 BITMAP
记录用户在线状态。
String类型的bitcount(1.6.6的bitmap数据结构介绍)
字符是以8位二进制存储的
命令:
SETBIT online_users userId 1
BITCOUNT online_users
Java 实现:
/**
* 标记用户为在线状态
*
* @param userId 用户 ID
*/
public void markOnline(String userId) {
redisTemplate.opsForValue().setBit("online_users", Long.parseLong(userId), true); // SETBIT
}
/**
* 获取在线用户数量
*
* @return 在线用户数量
*/
public long countOnlineUsers() {
return redisTemplate.opsForValue().bitCount("online_users"); // BITCOUNT
}
8. 购物车
String 或hash。所有String可以做的hash都可以做
Redis 使用 HASH
类型实现购物车功能。
key:用户id;field:商品id;value:商品数量。
+1:hincr。-1:hdecr。删除:hdel。全选:hgetall。商品数:hlen。
命令:
HSET cart:userId productId quantity
HGETALL cart:userId
Java 实现:
/**
* 将商品加入购物车
*
* @param userId 用户 ID
* @param productId 商品 ID
* @param quantity 商品数量
*/
public void addToCart(String userId, String productId, int quantity) {
redisUtil.setHash("cart:" + userId, productId, String.valueOf(quantity)); // HSET
}
/**
* 获取购物车所有商品
*
* @param userId 用户 ID
* @return 商品列表
*/
public Map<Object, Object> getCart(String userId) {
return redisTemplate.opsForHash().entries("cart:" + userId); // HGETALL
}
好的,以下是剩余部分的实现代码,包含了消息队列、点赞、签到、商品标签、筛选、推荐模型、排行榜等功能,均保留了原有 Redis 命令并添加详细注释。
9. 用户消息时间线 (Timeline)
Redis 使用 LIST
类型实现消息时间线,消息按插入顺序排列。
命令:
LPUSH timeline:userId message
LRANGE timeline:userId 0 9
Java 实现:
/**
* 将消息加入用户时间线
*
* @param userId 用户 ID
* @param message 消息内容
*/
public void addToTimeline(String userId, String message) {
String key = "timeline:" + userId; // 构造 Redis Key
redisTemplate.opsForList().leftPush(key, message); // LPUSH
}
/**
* 获取用户时间线最近的 N 条消息
*
* @param userId 用户 ID
* @param count 获取的消息数量
* @return 消息列表
*/
public List<String> getTimeline(String userId, int count) {
String key = "timeline:" + userId; // 构造 Redis Key
return redisTemplate.opsForList().range(key, 0, count - 1); // LRANGE
}
10. 消息队列
List提供了两个阻塞的弹出操作:blpop/brpop,可以设置超时时间
-
blpop:blpop key1 timeout 移除并获取列表的第一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
-
brpop:brpop key1 timeout 移除并获取列表的最后一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
上面的操作。其实就是java的阻塞队列。学习的东西越多。学习成本越低
-
队列:先进先除:rpush blpop,左头右尾,右边进入队列,左边出队列
-
栈:先进后出:rpush brpop
Redis 使用 LIST
类型实现队列功能,支持阻塞操作。
命令:
RPUSH queue:messages message
BLPOP queue:messages 0
Java 实现:
/**
* 将消息放入消息队列
*
* @param message 消息内容
*/
public void enqueueMessage(String message) {
String key = "queue:messages"; // 队列 Key
redisTemplate.opsForList().rightPush(key, message); // RPUSH
}
/**
* 从消息队列中取出消息(阻塞操作)
*
* @return 消息内容
*/
public String dequeueMessage() {
String key = "queue:messages"; // 队列 Key
return redisTemplate.opsForList().leftPop(key, 0, TimeUnit.SECONDS); // BLPOP
}
11. 抽奖
Redis 使用 SPOP
随机弹出集合中的元素。
命令:
SADD lottery:participants userId
SPOP lottery:participants
Java 实现:
/**
* 添加用户到抽奖池
*
* @param userId 用户 ID
*/
public void addToLottery(String userId) {
String key = "lottery:participants"; // 抽奖池 Key
redisTemplate.opsForSet().add(key, userId); // SADD
}
/**
* 随机抽取一位获奖者
*
* @return 获奖者的用户 ID
*/
public String drawWinner() {
String key = "lottery:participants"; // 抽奖池 Key
return redisTemplate.opsForSet().pop(key); // SPOP
}
12. 点赞、签到、打卡
假如微博ID是t1001,用户ID是u3001
用 like:t1001 来维护 t1001 这条微博的所有点赞用户
点赞了这条微博:sadd like:t1001 u3001
取消点赞:srem like:t1001 u3001
是否点赞:sismember like:t1001 u3001
点赞的所有用户:smembers like:t1001
点赞数:scard like:t1001
Redis 使用 SET
类型记录点赞用户。
命令:
SADD like:postId userId
SREM like:postId userId
SCARD like:postId
Java 实现:
/**
* 点赞
*
* @param postId 帖子 ID
* @param userId 用户 ID
*/
public void likePost(String postId, String userId) {
String key = "like:" + postId; // 点赞集合 Key
redisTemplate.opsForSet().add(key, userId); // SADD
}
/**
* 取消点赞
*
* @param postId 帖子 ID
* @param userId 用户 ID
*/
public void unlikePost(String postId, String userId) {
String key = "like:" + postId; // 点赞集合 Key
redisTemplate.opsForSet().remove(key, userId); // SREM
}
/**
* 获取点赞数
*
* @param postId 帖子 ID
* @return 点赞人数
*/
public long getLikes(String postId) {
String key = "like:" + postId; // 点赞集合 Key
return redisTemplate.opsForSet().size(key); // SCARD
}
13. 商品标签
Redis 使用 SET
类型存储商品标签。
命令:
SADD tags:itemId tag
SMEMBERS tags:itemId
Java 实现:
/**
* 添加商品标签
*
* @param itemId 商品 ID
* @param tag 标签内容
*/
public void addTagToItem(String itemId, String tag) {
String key = "tags:" + itemId; // 商品标签 Key
redisTemplate.opsForSet().add(key, tag); // SADD
}
/**
* 获取商品的所有标签
*
* @param itemId 商品 ID
* @return 标签列表
*/
public Set<String> getItemTags(String itemId) {
String key = "tags:" + itemId; // 商品标签 Key
return redisTemplate.opsForSet().members(key); // SMEMBERS
}
14. 商品筛选
Redis 使用 SET
类型实现筛选功能,通过交集、并集、差集操作过滤商品。
命令:
SINTER set1 set2
SUNION set1 set2
SDIFF set1 set2
Java 实现:
/**
* 获取多个条件筛选的交集(AND 筛选)
*
* @param keys 条件对应的 Key 集合
* @return 满足条件的商品集合
*/
public Set<String> intersectKeys(String... keys) {
return redisTemplate.opsForSet().intersect(Arrays.asList(keys)); // SINTER
}
/**
* 获取多个条件筛选的并集(OR 筛选)
*
* @param keys 条件对应的 Key 集合
* @return 满足条件的商品集合
*/
public Set<String> unionKeys(String... keys) {
return redisTemplate.opsForSet().union(Arrays.asList(keys)); // SUNION
}
/**
* 获取多个条件筛选的差集(NOT 筛选)
*
* @param keys 条件对应的 Key 集合
* @return 满足条件的商品集合
*/
public Set<String> diffKeys(String... keys) {
return redisTemplate.opsForSet().difference(Arrays.asList(keys)); // SDIFF
}
15. 用户关注和推荐模型
Redis 使用 SET
类型实现关注、粉丝和推荐功能。
命令:
SADD user:follow:userId targetUserId
SINTER user:follow:userId user:follow:targetUserId
Java 实现:
/**
* 关注用户
*
* @param userId 当前用户 ID
* @param targetUserId 被关注用户 ID
*/
public void followUser(String userId, String targetUserId) {
String key = "user:follow:" + userId; // 关注列表 Key
redisTemplate.opsForSet().add(key, targetUserId); // SADD
}
/**
* 获取共同关注用户(推荐好友)
*
* @param userId 当前用户 ID
* @param otherUserId 其他用户 ID
* @return 共同关注的用户集合
*/
public Set<String> getCommonFollow(String userId, String otherUserId) {
String key1 = "user:follow:" + userId; // 用户 1 的关注列表
String key2 = "user:follow:" + otherUserId; // 用户 2 的关注列表
return redisTemplate.opsForSet().intersect(key1, key2); // SINTER
}
16. 排行榜
Redis 使用 ZSET
类型实现排行榜功能。
命令:
ZINCRBY leaderboard:date score userId
ZREVRANGE leaderboard:date 0 9 WITHSCORES
Java 实现:
/**
* 增加用户分数
*
* @param date 排行榜日期(如 2025-01-08)
* @param userId 用户 ID
* @param score 增加的分数
*/
public void increaseScore(String date, String userId, double score) {
String key = "leaderboard:" + date; // 排行榜 Key
redisTemplate.opsForZSet().incrementScore(key, userId, score); // ZINCRBY
}
/**
* 获取排行榜前 N 名
*
* @param date 排行榜日期(如 2025-01-08)
* @param count 获取的排名数量
* @return 排行榜列表(包含用户 ID 和分数)
*/
public Set<ZSetOperations.TypedTuple<String>> getTopRank(String date, int count) {
String key = "leaderboard:" + date; // 排行榜 Key
return redisTemplate.opsForZSet().reverseRangeWithScores(key, 0, count - 1); // ZREVRANGE WITHSCORES
}