leniu-redis-cache
SKILL.md
leniu-redis-cache
适用于 leniu-tengyun-core 项目的 Redis 缓存和分布式锁操作。
RedisUtil 工具类
位置:net.xnzn.core.common.redis.RedisUtil
String 操作
import net.xnzn.core.common.redis.RedisUtil;
// 设置 String(无过期时间)
RedisUtil.setString("key", "value");
// 设置 String(带过期时间,单位秒)
RedisUtil.setString("key", "value", 3600L);
// 获取 String
String value = RedisUtil.getString("key");
// 存在则更新,不存在则设置
Boolean updated = RedisUtil.setIfPresentString("key", "new-value", 3600L);
Object 操作
// 设置对象(无过期时间)
RedisUtil.setObj("user:123", user);
// 设置对象(带过期时间)
RedisUtil.setObj("user:123", user, 1800L);
// 获取对象
Object user = RedisUtil.getObj("user:123");
User userObj = (User) RedisUtil.getObj("user:123");
删除操作
// 删除指定 key
RedisUtil.delete("user:123");
// 批量删除(匹配模式)
RedisUtil.deleteByPattern("cache:user:*");
自增/自减
// 自增(返回递增后的值)
Integer count = RedisUtil.incr("counter", 3600L);
// 自减(返回递减后的值)
Integer count = RedisUtil.decr("counter", 3600L);
// 仅获取当前值(不增减)
Integer current = RedisUtil.getRedisAtomicInteger("counter", null, null);
分布式锁
import org.redisson.api.RLock;
// 获取锁对象
RLock lock = RedisUtil.getLock("lock:order:123");
// 标准用法(推荐):lock() 直接阻塞获取
RLock lock = RedisUtil.getLock("lock:order:123");
lock.lock();
try {
// 执行业务逻辑
doBusiness();
} catch (Exception e) {
log.error("业务处理异常", e);
} finally {
// ✅ 安全释放锁:双重检查,防止重复 unlock
try {
if (lock.isHeldByCurrentThread() && lock.isLocked()) {
lock.unlock();
}
} catch (Exception e) {
log.error("解锁异常", e);
}
}
// tryLock 用法(不阻塞,失败直接抛异常)
RLock lock2 = RedisUtil.getLock("lock:order:123");
if (!lock2.tryLock()) {
throw new LeException("订单正在处理中,请稍后重试");
}
try {
doBusiness();
} finally {
try {
if (lock2.isHeldByCurrentThread() && lock2.isLocked()) {
lock2.unlock();
}
} catch (Exception e) {
log.error("解锁异常", e);
}
}
// 检查锁状态
boolean locked = RedisUtil.isLock("lock:order:123");
已弃用的方法(避免代码扫描问题,请使用 getLock + lock/unlock):
// 不推荐使用(已弃用)
RedisUtil.tryLock("lock:key", 10, 30);
RedisUtil.lock("lock:key");
RedisUtil.unLock("lock:key");
ZSet 操作
// 添加到有序集合
RedisUtil.zAdd("ranking:weekly", "user123", 95.5);
RedisUtil.zAdd("ranking:weekly", "user456", 88.3);
// 获取排名数据(带分数)
Set<ZSetOperations.TypedTuple<Object>> ranking = RedisUtil.zGetList("ranking:weekly", 0, 9);
// 从有序集合中删除
RedisUtil.zRemove("ranking:weekly", "user123");
批量操作
// 批量保存
Map<String, Object> map = new HashMap<>();
map.put("key1", "value1");
map.put("key2", "value2");
RedisUtil.multiSet(map);
// 批量获取
List keys = Arrays.asList("key1", "key2", "key3");
List values = RedisUtil.multiGet(keys);
Key 查询
// 匹配 key
List<String> keys = RedisUtil.keysByPattern("cache:*");
// 检查锁是否锁定
boolean isLocked = RedisUtil.isLock("lock:order:123");
缓存配置
最大缓存时间
可以通过配置限制缓存的最大过期时间:
spring:
redis:
# 自定义最大缓存时间(秒)
custom-max-expiration-second: 86400 # 24小时
缓存 Key 命名规范
| 类型 | 命名格式 | 示例 |
|---|---|---|
| 用户信息 | user:{id} |
user:12345 |
| 用户列表缓存 | user:list:{params_hash} |
user:list:abc123 |
| 权限缓存 | permissions:subject_id:{id} |
permissions:subject_id:123 |
| 角色缓存 | roles:subject_id:{id} |
roles:subject_id:123 |
| 租户配置 | tenant:config:{id} |
tenant:config:123 |
| 字典缓存 | dict:{type}:{key} |
dict:sys_yes_no:1 |
| 分布式锁 | lock:{module}:{id} |
lock:order:123 |
| 限流器 | rate:{user_id}:{api} |
rate:123:/api/order/create |
典型场景
缓存击穿保护
public User getUserById(Long userId) {
String key = "user:" + userId;
// 先查缓存
Object cached = RedisUtil.getObj(key);
if (cached != null) {
return (User) cached;
}
// 使用分布式锁防止缓存击穿
RLock lock = RedisUtil.getLock("lock:user:" + userId);
try {
if (lock.tryLock(5, 30, TimeUnit.SECONDS)) {
try {
// 双重检查
cached = RedisUtil.getObj(key);
if (cached != null) {
return (User) cached;
}
// 查询数据库
User user = userMapper.selectById(userId);
if (user != null) {
RedisUtil.setObj(key, user, 3600L);
}
return user;
} finally {
lock.unlock();
}
}
return userMapper.selectById(userId);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return userMapper.selectById(userId);
}
}
限流器
public boolean checkRateLimit(Long userId, String api, int limit, int seconds) {
String key = String.format("rate:%d:%s", userId, api);
Integer count = RedisUtil.incr(key, (long) seconds);
// 首次请求设置过期时间
if (count == 1) {
RedisUtil.setString(key, count.toString(), (long) seconds);
}
return count <= limit;
}
分布式任务去重
public void executeTask(String taskId) {
String key = "task:executing:" + taskId;
// setNx 防止重复执行
boolean acquired = RedisUtil.setNx(key, "1", 300);
if (!acquired) {
log.warn("任务正在执行中: {}", taskId);
return;
}
try {
// 执行任务
doTask(taskId);
} finally {
RedisUtil.delete(key);
}
}
排行榜
// 添加分数
public void addScore(Long userId, Double score) {
RedisUtil.zAdd("ranking:daily", String.valueOf(userId), score);
}
// 获取 TOP 10
public List<RankingItem> getTop10() {
Set<ZSetOperations.TypedTuple<Object>> ranking =
RedisUtil.zGetList("ranking:daily", 0, 9);
return ranking.stream()
.map(tuple -> new RankingItem(
Long.parseLong(tuple.getValue().toString()),
tuple.getScore()
))
.sorted(Comparator.comparing(RankingItem::getScore).reversed())
.collect(Collectors.toList());
}
// 获取用户排名
public Long getUserRank(Long userId) {
// 需要 RedisUtil 扩展,或使用 RedisTemplate
Long rank = redisTemplate.opsForZSet().rank("ranking:daily", String.valueOf(userId));
return rank != null ? rank + 1 : null;
}
与 RuoYi-Plus 的区别
| 特性 | RuoYi-Plus | leniu-tengyun-core |
|---|---|---|
| 工具类 | RedisUtils |
RedisUtil |
| 分布式锁 | RedisUtils.lock() |
RedisUtil.getLock() |
| 设置对象 | RedisUtils.setCacheObject() |
RedisUtil.setObj() |
| 获取对象 | RedisUtils.getCacheObject() |
RedisUtil.getObj() |
| 删除 | RedisUtils.deleteObject() |
RedisUtil.delete() |
| ZSet 操作 | RedisUtils.getCacheZSetRank() |
RedisUtil.zAdd/zGetList() |
| 缓存注解 | @Cacheable |
未使用注解方式 |
| 锁释放 | RedisUtils.unlock() |
RLock.unlock() |
注意事项
setNx用于防止重复执行,返回true表示设置成功- 分布式锁必须使用
try-finally确保 unlock 执行 - 自增/自减操作可指定过期时间,首次自增时设置
- 过期时间单位统一为秒
- 批量删除使用
deleteByPattern会扫描所有匹配 key,生产环境慎用 RedisUtil底层使用RedisTemplate和RedissonClient
Weekly Installs
3
Repository
xu-cell/ai-engi…ing-initGitHub Stars
9
First Seen
9 days ago
Security Audits
Installed on
gemini-cli3
github-copilot3
codex3
kimi-cli3
cursor3
amp3