
Redis 凭借其高性能、丰富的数据结构和灵活的部署方式,已成为现代应用架构中的关键组件。除了大家熟知的缓存功能,Redis 在众多业务场景中都能发挥重要作用。本文将系统梳理 Redis 的常见使用场景,深入分析每种场景的技术实现和最佳实践,帮助开发者充分发挥 Redis 的价值。
这是 Redis 最经典的应用场景。在电商、社交等高频访问场景中,将热点数据(如商品详情、用户信息)缓存到 Redis,可大幅降低数据库压力,提升系统响应速度。
// 商品信息缓存示例public ProductDTO getProductInfo(Long productId) { String cacheKey = "product:info:" + productId; // 尝试从缓存获取 ProductDTO product = (ProductDTO) redisTemplate.opsForValue().get(cacheKey); if (product != null) { return product; } // 缓存未命中,查询数据库 ProductDO productDO = productMapper.selectById(productId); if (productDO != null) { product = convertToDTO(productDO); // 写入缓存,设置30分钟过期 redisTemplate.opsForValue().set(cacheKey, product, 30, TimeUnit.MINUTES); } else { // 缓存空值,设置5分钟过期 redisTemplate.opsForValue().set(cacheKey, new ProductDTO(), 5, TimeUnit.MINUTES); } return product;}在分布式系统中,多个服务实例需要协同操作共享资源时,分布式锁是保证数据一致性的关键机制。Redis 的SET NX命令天然适合实现分布式锁。
// 分布式锁核心实现public boolean tryLock(String lockKey, long expireTime, TimeUnit unit) { String identifier = UUID.randomUUID().toString(); Boolean success = redisTemplate.opsForValue().setIfAbsent( lockKey, identifier, expireTime, unit ); if (Boolean.TRUE.equals(success)) { // 启动定时续期任务 startLockRenewer(lockKey, identifier, expireTime, unit); return true; } return false;}// 解锁Lua脚本private static final String UNLOCK_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] " + "then return redis.call('del', KEYS[1]) " + "else return 0 end";Redis 的原子递增命令INCR非常适合实现计数器功能,结合过期时间可实现接口限流。
// 接口限流实现public boolean isAllowed(String userId, String action, int maxCount, int period) { String key = "rate_limit:" + action + ":" + userId; Long count = redisTemplate.opsForValue().increment(key); if (count != null && count == 1) { // 首次访问,设置过期时间 redisTemplate.expire(key, period, TimeUnit.SECONDS); } return count != null && count <= maxCount;}// 阅读量统计public long incrementReadCount(Long articleId) { String key = "article:read_count:" + articleId; return redisTemplate.opsForValue().increment(key);}Redis 的 Sorted Set(有序集合)数据结构完美适配排行榜需求,支持按分数实时排序。
// 排行榜服务public class RankingService { // 添加/更新分数 public void updateScore(String rankKey, String member, double score) { redisTemplate.opsForZSet().add(rankKey, member, score); } // 获取Top N排名 public List<RankingItem> getTopRank(String rankKey, int topN) { Set<ZSetOperations.TypedTuple<Object>> tuples = redisTemplate.opsForZSet() .reverseRangeWithScores(rankKey, 0, topN - 1); List<RankingItem> result = new ArrayList<>(); int rank = 1; for (ZSetOperations.TypedTuple<Object> tuple : tuples) { result.add(new RankingItem( tuple.getValue().toString(), tuple.getScore(), rank++ )); } return result; } // 获取用户排名 public Long getUserRank(String rankKey, String member) { return redisTemplate.opsForZSet().reverseRank(rankKey, member); }}在分布式系统中,使用 Redis 存储用户会话信息,可实现多服务实例间的会话共享。
// 分布式会话管理public class RedisSessionManager { private static final String SESSION_PREFIX = "session:"; private static final int SESSION_EXPIRE_HOURS = 2; // 创建会话 public String createSession(UserDTO user) { String sessionId = UUID.randomUUID().toString(); String key = SESSION_PREFIX + sessionId; redisTemplate.opsForValue().set(key, user, SESSION_EXPIRE_HOURS, TimeUnit.HOURS); return sessionId; } // 获取会话 public UserDTO getSession(String sessionId) { if (sessionId == null) return null; String key = SESSION_PREFIX + sessionId; UserDTO user = (UserDTO) redisTemplate.opsForValue().get(key); if (user != null) { // 刷新过期时间 redisTemplate.expire(key, SESSION_EXPIRE_HOURS, TimeUnit.HOURS); } return user; }}利用 Redis 的 List 数据结构可实现简单的消息队列,适合对消息可靠性要求不高的场景。
// 简单消息队列实现public class RedisMessageQueue { private String queueKey; public RedisMessageQueue(String queueKey) { this.queueKey = queueKey; } // 发送消息 public Long sendMessage(String message) { return redisTemplate.opsForList().leftPush(queueKey, message); } // 接收消息(阻塞式) public String receiveMessage(long timeout, TimeUnit unit) { List<Object> messages = redisTemplate.opsForList().rightPop(queueKey, timeout, unit); return messages != null && !messages.isEmpty() ? messages.get(0).toString() : null; } // 消息监听 public void listenMessage(Consumer<String> consumer) { new Thread(() -> { while (true) { try { String message = receiveMessage(1, TimeUnit.MINUTES); if (message != null) { consumer.accept(message); } } catch (Exception e) { // 异常处理 } } }).start(); }}Redis 的 Bitmap(位图)是一种节省空间的二进制数据结构,适合存储大量布尔值信息。
// 签到功能实现public class CheckInService { // 用户签到 public boolean checkIn(Long userId) { String key = "checkin:" + userId + ":" + LocalDate.now().getYear(); int dayOfYear = LocalDate.now().getDayOfYear() - 1; // 从0开始 return redisTemplate.opsForValue().setBit(key, dayOfYear, true); } // 获取当月签到次数 public long getMonthlyCheckInCount(Long userId) { LocalDate today = LocalDate.now(); String key = "checkin:" + userId + ":" + today.getYear(); int lastDay = today.lengthOfMonth(); return redisTemplate.execute( (RedisCallback<Long>) connection -> connection.bitCount(key.getBytes(), 0, lastDay) ); } // 获取连续签到天数 public int getContinuousCheckInDays(Long userId) { LocalDate today = LocalDate.now(); String key = "checkin:" + userId + ":" + today.getYear(); int dayOfYear = today.getDayOfYear() - 1; int continuousDays = 0; // 从今天开始往前查,直到遇到未签到的日期 for (int i = 0; i <= dayOfYear; i++) { Boolean isCheckIn = redisTemplate.opsForValue().getBit(key, dayOfYear - i); if (Boolean.TRUE.equals(isCheckIn)) { continuousDays++; } else { break; } } return continuousDays; }}Redis 的 GEO 数据结构专门用于存储和操作地理坐标信息,适合 LBS(基于位置的服务)场景。
// 附近店铺查询public class NearbyStoreService { private static final String GEO_KEY = "stores:locations"; // 添加店铺位置 public void addStoreLocation(Long storeId, double longitude, double latitude) { redisTemplate.opsForGeo().add(GEO_KEY, new Point(longitude, latitude), storeId.toString()); } // 搜索附近的店铺 public List<StoreLocation> findNearbyStores(double longitude, double latitude, double radius) { // 搜索半径radius公里内的店铺,返回前20名 GeoResults<GeoLocation<Object>> results = redisTemplate.opsForGeo() .radius(GEO_KEY, new Circle(new Point(longitude, latitude), new Distance(radius, Metrics.KILOMETERS)), RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().limit(20)); List<StoreLocation> stores = new ArrayList<>(); for (GeoResult<GeoLocation<Object>> result : results) { StoreLocation location = new StoreLocation(); location.setStoreId(Long.valueOf(result.getContent().getName())); location.setDistance(result.getDistance().getValue()); stores.add(location); } return stores; }}Redis 的 HyperLogLog 是一种概率性数据结构,用于高效统计基数(不重复元素数量)。
// UV统计实现public class UvStatService { // 记录访问用户 public void recordVisit(Long userId) { String key = "uv:stat:" + LocalDate.now().toString(); redisTemplate.opsForHyperLogLog().add(key, userId); } // 获取当日UV public long getTodayUv() { String key = "uv:stat:" + LocalDate.now().toString(); return redisTemplate.opsForHyperLogLog().size(key); } // 获取指定日期范围的UV public long getUvByDateRange(LocalDate start, LocalDate end) { List<String> keys = new ArrayList<>(); LocalDate date = start; while (!date.isAfter(end)) { keys.add("uv:stat:" + date.toString()); date = date.plusDays(1); } String destKey = "uv:stat:temp:" + System.currentTimeMillis(); // 合并多个HyperLogLog redisTemplate.opsForHyperLogLog().union(destKey, keys.toArray(new String[0])); long total = redisTemplate.opsForHyperLogLog().size(destKey); // 删除临时key redisTemplate.delete(destKey); return total; }}Redis 的多样化数据结构使其能够适应多种业务场景,在实际应用中,还需注意以下最佳实践:
通过灵活运用 Redis 的各种特性,不仅能解决实际业务问题,还能显著提升系统性能和可扩展性。在选择使用场景时,需结合业务需求和 Redis 的特性综合考量,才能充分发挥其价值。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。