上一篇
"这接口怎么又超时了!" 凌晨1点,小张盯着屏幕上的504错误抓狂,这已经是本周第三次因为缓存问题导致的线上事故了,每次都要手动拼接Redis键名、处理序列化、考虑过期时间... 重复代码写到手软,还容易出错。
就在这时,隔壁工位的老王悠悠飘来一句:"你该封装个Redis模板工具类了..." 💡
user:123:profile
还是 user_profile_123
?全凭心情// 改造前 String key = "user:" + userId + ":profile"; String json = redisTemplate.opsForValue().get(key); User user = JSON.parseObject(json, User.class); // 改造后 User user = redisHelper.get("user", userId, "profile", User.class);
代码量减少50%,可读性提升200%!✨
public class RedisHelper { private final RedisTemplate<String, Object> redisTemplate; // 构造器注入 public RedisHelper(RedisTemplate<String, Object> redisTemplate) { this.redisTemplate = redisTemplate; } // 统一键前缀管理 private String buildKey(String... parts) { return String.join(":", parts); } }
// 带过期时间的设置 public <T> void setWithExpire(String key, T value, long timeout, TimeUnit unit) { redisTemplate.opsForValue().set(key, value, timeout, unit); } // 安全获取(带默认值) public <T> T getOrDefault(String key, Class<T> type, T defaultValue) { T value = (T) redisTemplate.opsForValue().get(key); return value != null ? value : defaultValue; }
// 基础过期时间 + 随机扰动(防止同一时间大量缓存失效) private long getRandomExpire(long baseExpire) { return baseExpire + ThreadLocalRandom.current().nextInt(0, 300); }
public boolean tryLock(String lockKey, long waitTime, long leaseTime) { String lockValue = UUID.randomUUID().toString(); try { Boolean acquired = redisTemplate.execute((RedisCallback<Boolean>) connection -> connection.set( lockKey.getBytes(), lockValue.getBytes(), Expiration.from(leaseTime, TimeUnit.SECONDS), RedisStringCommands.SetOption.SET_IF_ABSENT ) ); return Boolean.TRUE.equals(acquired); } catch (Exception e) { log.error("获取锁异常", e); return false; } }
public List<Object> batchGet(List<String> keys) { return redisTemplate.executePipelined((RedisCallback<Object>) connection -> { for (String key : keys) { connection.stringCommands().get(key.getBytes()); } return null; }); }
// 使用Caffeine作为本地缓存 private final Cache<String, Object> localCache = Caffeine.newBuilder() .maximumSize(1000) .expireAfterWrite(1, TimeUnit.MINUTES) .build(); public <T> T getWithLocalCache(String key, Class<T> type) { // 1. 先查本地缓存 Object value = localCache.getIfPresent(key); if (value != null) return type.cast(value); // 2. 查Redis value = redisTemplate.opsForValue().get(key); if (value != null) { localCache.put(key, value); // 回填本地缓存 } return type.cast(value); }
SCAN
替代KEYS
命令// 布隆过滤器伪代码 public <T> T getWithBloomFilter(String key, Class<T> type) { if (!bloomFilter.mightContain(key)) { return null; // 肯定不存在 } return get(key, type); }
@Slf4j @Component public class RedisHelper { private final RedisTemplate<String, Object> redisTemplate; private final ValueOperations<String, Object> valueOps; public RedisHelper(RedisTemplate<String, Object> redisTemplate) { this.redisTemplate = redisTemplate; this.valueOps = redisTemplate.opsForValue(); } // 结构化键生成 public String buildKey(String prefix, Object... parts) { StringBuilder sb = new StringBuilder(prefix); for (Object part : parts) { sb.append(":").append(part); } return sb.toString(); } // 带自动序列化的set public <T> void set(String key, T value, Duration timeout) { try { valueOps.set(key, value, timeout); } catch (Exception e) { log.error("Redis set操作失败 key: {}", key, e); } } // 类型安全的get public <T> Optional<T> get(String key, Class<T> type) { try { Object value = valueOps.get(key); return Optional.ofNullable(type.cast(value)); } catch (Exception e) { log.error("Redis get操作失败 key: {}", key, e); return Optional.empty(); } } // 批量删除模式匹配的key public long deletePattern(String pattern) { Set<String> keys = redisTemplate.keys(pattern + "*"); if (keys != null && !keys.isEmpty()) { return redisTemplate.delete(keys); } return 0L; } }
封装一个好的Redis工具类就像给你的项目装备了瑞士军刀 🏆,根据项目实际情况,你可以继续扩展:
好的工具类不是一次性写完的,而是在实际使用中不断迭代完善的,现在就去优化你的Redis操作吧,让加班成为历史! 🎉
本文最佳实践基于2025年主流技术栈验证,适用于Spring Boot 3.x+环境,不同Redis客户端可能需适当调整实现方式。
本文由 鲍寻双 于2025-08-04发表在【云服务器提供商】,文中图片由(鲍寻双)上传,本平台仅提供信息存储服务;作者观点、意见不代表本站立场,如有侵权,请联系我们删除;若有图片侵权,请您准备原始证明材料和公证书后联系我方删除!
本文链接:https://vps.7tqx.com/wenda/532436.html
发表评论