RedisUtils API 参考
RedisUtils 是项目封装的 Redis 通用工具类,基于 RedisTemplate 提供类型安全、参数校验、日志记录的便捷操作。
位置: blog-common/src/main/java/com/blog/common/utils/RedisUtils.java
🎯 核心特性
| 特性 | 说明 |
|---|---|
| 参数校验 | 使用 Guava Preconditions,防止空值和非法参数 |
| 日志记录 | SLF4J 参数化日志,便于问题排查 |
| 类型安全 | Optional 包装返回值,避免空指针 |
| 批量操作 | 支持 MGET/MSET,减少网络往返 |
| 防雪崩 | setWithRandomTTL 支持随机 TTL |
📖 通用操作
hasKey
判断 key 是否存在
boolean exists = redisUtils.hasKey("user:123");
| 参数 | 类型 | 说明 |
|---|---|---|
| key | String | 键(非空) |
| 返回 | boolean | 存在返回 true |
expire
设置过期时间
redisUtils.expire("user:123", 30, TimeUnit.MINUTES);
| 参数 | 类型 | 说明 |
|---|---|---|
| key | String | 键 |
| timeout | long | 过期时间(> 0) |
| unit | TimeUnit | 时间单位 |
| 返回 | boolean | 设置成功返回 true |
delete
删除键
// 删除单个
redisUtils.delete("user:123");
// 批量删除
redisUtils.delete(List.of("user:1", "user:2", "user:3"));
| 参数 | 类型 | 说明 |
|---|---|---|
| key / keys | String / Collection | 要删除的键 |
| 返回 | boolean / Long | 是否成功 / 删除数量 |
📝 String 操作
set
设置值
// 永不过期(不推荐)
redisUtils.set("key", value);
// 带过期时间(推荐)
redisUtils.set("key", value, 30, TimeUnit.MINUTES);
| 参数 | 类型 | 说明 |
|---|---|---|
| key | String | 键 |
| value | Object | 值(非空) |
| timeout | long | 过期时间(可选) |
| unit | TimeUnit | 时间单位(可选) |
setIfAbsent
只有 key 不存在时才设置(分布式锁)
boolean locked = redisUtils.setIfAbsent(
"lock:order:123",
"locked",
10,
TimeUnit.SECONDS
);
if (locked) {
try {
// 执行临界区代码
} finally {
redisUtils.delete("lock:order:123");
}
}
| 参数 | 类型 | 说明 |
|---|---|---|
| key | String | 锁键 |
| value | Object | 锁值 |
| timeout | long | 锁超时时间 |
| unit | TimeUnit | 时间单位 |
| 返回 | boolean | true=获取锁成功 |
分布式锁
这是实现简单分布式锁的核心方法,对应 Redis 的 SET NX EX 命令。
get
获取值
// 基础获取(返回 Object)
Object value = redisUtils.get("key");
// 类型安全获取
UserDTO user = redisUtils.get("user:123", UserDTO.class);
// Optional 包装(推荐)
Optional<UserDTO> userOpt = redisUtils.getOptional("user:123", UserDTO.class);
userOpt.ifPresent(u -> System.out.println(u.getUsername()));
| 方法 | 返回类型 | 说明 |
|---|---|---|
get(key) | Object | 可能为 null |
get(key, clazz) | T | 类型转换,可能为 null |
getOptional(key, clazz) | Optional<T> | 避免 null,推荐使用 |
increment / decrement
原子计数器
// 递增(文章浏览量)
Long views = redisUtils.increment("article:views:123");
// 递减(库存扣减)
Long stock = redisUtils.decrement("product:stock:456");
| 方法 | 返回 | 说明 |
|---|---|---|
increment(key) | Long | 递增后的值 |
decrement(key) | Long | 递减后的值 |
🗂️ Hash 操作
hSet / hGet
单个字段操作
// 设置字段
redisUtils.hSet("user:123", "name", "张三");
redisUtils.hSet("user:123", "age", 25);
// 获取字段
Object name = redisUtils.hGet("user:123", "name"); // "张三"
hSetAll / hGetAll
批量操作
// 批量设置
Map<String, Object> userMap = Map.of(
"name", "张三",
"age", 25,
"email", "zhangsan@example.com"
);
redisUtils.hSetAll("user:123", userMap);
// 批量获取
Map<Object, Object> result = redisUtils.hGetAll("user:123");
hDel
删除字段
Long deleted = redisUtils.hDel("user:123", "age", "email");
// deleted = 2
📦 Set 操作
sAdd / sMembers
// 添加元素(自动去重)
redisUtils.sAdd("user:123:tags", "java", "spring", "redis");
// 获取所有元素
Set<Object> tags = redisUtils.sMembers("user:123:tags");
// ["java", "spring", "redis"]
使用场景:标签、关注列表、点赞用户集合
📋 List 操作
lPush / rPop
队列操作
// 从左侧推入(入队)
redisUtils.lPush("task:queue", task1);
redisUtils.lPush("task:queue", task2);
// 从右侧弹出(出队)
Object task = redisUtils.rPop("task:queue");
使用场景:消息队列、任务队列
lRange / lSize
// 获取范围元素(0 到 -1 表示全部)
List<Object> tasks = redisUtils.lRange("task:queue", 0, -1);
// 获取列表长度
Long size = redisUtils.lSize("task:queue");
⚡ 批量操作
mGet
批量获取(性能优化)
List<String> keys = List.of("user:1", "user:2", "user:3");
List<Object> values = redisUtils.mGet(keys);
// 顺序与 keys 一致,不存在的键对应 null
性能对比
单次获取:3 次网络请求
批量获取:1 次网络请求(节省 2 次 RTT)
mSet
批量设置
Map<String, Object> data = Map.of(
"config:a", "value1",
"config:b", "value2",
"config:c", "value3"
);
redisUtils.mSet(data);
限制:批量操作最多 100 个键,防止慢查询。
🛡️ 高级操作
setWithRandomTTL
防缓存雪崩
redisUtils.setWithRandomTTL(
"article:123",
article,
30, // 基础 TTL: 30 分钟
TimeUnit.MINUTES,
10 // 随机偏移: ±10%
);
// 实际 TTL 范围:27~33 分钟
| 参数 | 类型 | 说明 |
|---|---|---|
| key | String | 键 |
| value | Object | 值 |
| baseTimeout | long | 基础过期时间 |
| unit | TimeUnit | 时间单位 |
| randomPercent | int | 随机百分比(0-100) |
原理:在基础 TTL 上添加随机偏移,避免大量缓存同时过期导致数据库压力骤增。
📊 使用场景汇总
| 场景 | 方法 | 示例 |
|---|---|---|
| 用户信息缓存 | set + get | set("user:123", user, 30, MINUTES) |
| 分布式锁 | setIfAbsent | setIfAbsent("lock:xxx", "1", 10, SECONDS) |
| 浏览量计数 | increment | increment("article:views:123") |
| 用户属性 | hSet + hGet | hSet("user:123", "name", "张三") |
| 标签集合 | sAdd + sMembers | sAdd("tags:123", "java", "spring") |
| 任务队列 | lPush + rPop | lPush("queue", task) |
| 批量查询 | mGet | mGet(List.of("user:1", "user:2")) |
| 防雪崩 | setWithRandomTTL | 见上文 |
⚠️ 注意事项
1. 总是设置 TTL
// ❌ 不推荐
redisUtils.set("key", value);
// ✅ 推荐
redisUtils.set("key", value, 30, TimeUnit.MINUTES);
2. 批量操作有限制
// ❌ 超过限制会抛异常
redisUtils.mGet(hugeListWith200Keys); // IllegalArgumentException
// ✅ 分批处理
Lists.partition(allKeys, 100).forEach(batch -> {
redisUtils.mGet(batch);
});
3. 使用 Optional 避免 NPE
// ❌ 可能 NPE
UserDTO user = redisUtils.get("user:123", UserDTO.class);
user.getUsername(); // 如果 user 为 null?
// ✅ 安全
redisUtils.getOptional("user:123", UserDTO.class)
.ifPresent(u -> System.out.println(u.getUsername()));