Redis使用场景

Redis使用场景

概述

Redis作为高性能的内存数据库,在现代应用架构中有着广泛的应用场景。本文详细介绍Redis在缓存、分布式锁、消息队列、计数器、会话存储等场景中的应用实践。

缓存应用

数据缓存

查询结果缓存

java
@Service
public class UserService {
    
    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    public User getUserById(Long userId) {
        String cacheKey = "user:" + userId;
        
        // 先从缓存获取
        User user = (User) redisTemplate.opsForValue().get(cacheKey);
        if (user != null) {
            return user;
        }
        
        // 缓存未命中,从数据库查询
        user = userRepository.findById(userId);
        if (user != null) {
            // 写入缓存,设置过期时间
            redisTemplate.opsForValue().set(cacheKey, user, Duration.ofHours(1));
        }
        
        return user;
    }
    
    public void updateUser(User user) {
        // 更新数据库
        userRepository.save(user);
        
        // 删除缓存,保证数据一致性
        String cacheKey = "user:" + user.getId();
        redisTemplate.delete(cacheKey);
    }
}
@Service
public class UserService {
    
    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    public User getUserById(Long userId) {
        String cacheKey = "user:" + userId;
        
        // 先从缓存获取
        User user = (User) redisTemplate.opsForValue().get(cacheKey);
        if (user != null) {
            return user;
        }
        
        // 缓存未命中,从数据库查询
        user = userRepository.findById(userId);
        if (user != null) {
            // 写入缓存,设置过期时间
            redisTemplate.opsForValue().set(cacheKey, user, Duration.ofHours(1));
        }
        
        return user;
    }
    
    public void updateUser(User user) {
        // 更新数据库
        userRepository.save(user);
        
        // 删除缓存,保证数据一致性
        String cacheKey = "user:" + user.getId();
        redisTemplate.delete(cacheKey);
    }
}

页面缓存

java
@RestController
public class ProductController {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    @GetMapping("/products/{categoryId}")
    public String getProductList(@PathVariable Long categoryId, HttpServletResponse response) {
        String cacheKey = "product_list:" + categoryId;
        
        // 检查缓存
        String cachedHtml = redisTemplate.opsForValue().get(cacheKey);
        if (cachedHtml != null) {
            response.setHeader("X-Cache", "HIT");
            return cachedHtml;
        }
        
        // 生成页面内容
        String html = generateProductListHtml(categoryId);
        
        // 缓存页面,设置5分钟过期
        redisTemplate.opsForValue().set(cacheKey, html, Duration.ofMinutes(5));
        
        response.setHeader("X-Cache", "MISS");
        return html;
    }
}
@RestController
public class ProductController {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    @GetMapping("/products/{categoryId}")
    public String getProductList(@PathVariable Long categoryId, HttpServletResponse response) {
        String cacheKey = "product_list:" + categoryId;
        
        // 检查缓存
        String cachedHtml = redisTemplate.opsForValue().get(cacheKey);
        if (cachedHtml != null) {
            response.setHeader("X-Cache", "HIT");
            return cachedHtml;
        }
        
        // 生成页面内容
        String html = generateProductListHtml(categoryId);
        
        // 缓存页面,设置5分钟过期
        redisTemplate.opsForValue().set(cacheKey, html, Duration.ofMinutes(5));
        
        response.setHeader("X-Cache", "MISS");
        return html;
    }
}

缓存策略

Cache-Aside模式

java
@Component
public class CacheAsidePattern {
    
    // 读取数据
    public User getUser(Long userId) {
        // 1. 先读缓存
        User user = getFromCache(userId);
        if (user != null) {
            return user;
        }
        
        // 2. 缓存未命中,读数据库
        user = getFromDatabase(userId);
        if (user != null) {
            // 3. 写入缓存
            putToCache(userId, user);
        }
        
        return user;
    }
    
    // 更新数据
    public void updateUser(User user) {
        // 1. 先更新数据库
        updateDatabase(user);
        
        // 2. 删除缓存
        deleteFromCache(user.getId());
    }
}
@Component
public class CacheAsidePattern {
    
    // 读取数据
    public User getUser(Long userId) {
        // 1. 先读缓存
        User user = getFromCache(userId);
        if (user != null) {
            return user;
        }
        
        // 2. 缓存未命中,读数据库
        user = getFromDatabase(userId);
        if (user != null) {
            // 3. 写入缓存
            putToCache(userId, user);
        }
        
        return user;
    }
    
    // 更新数据
    public void updateUser(User user) {
        // 1. 先更新数据库
        updateDatabase(user);
        
        // 2. 删除缓存
        deleteFromCache(user.getId());
    }
}

Write-Through模式

java
@Component
public class WriteThroughPattern {
    
    public void updateUser(User user) {
        // 同时更新缓存和数据库
        updateCache(user);
        updateDatabase(user);
    }
}
@Component
public class WriteThroughPattern {
    
    public void updateUser(User user) {
        // 同时更新缓存和数据库
        updateCache(user);
        updateDatabase(user);
    }
}

Write-Behind模式

java
@Component
public class WriteBehindPattern {
    
    private final Queue<User> writeQueue = new ConcurrentLinkedQueue<>();
    
    public void updateUser(User user) {
        // 1. 立即更新缓存
        updateCache(user);
        
        // 2. 异步更新数据库
        writeQueue.offer(user);
    }
    
    @Scheduled(fixedDelay = 1000)
    public void flushToDatabase() {
        List<User> batch = new ArrayList<>();
        User user;
        while ((user = writeQueue.poll()) != null && batch.size() < 100) {
            batch.add(user);
        }
        
        if (!batch.isEmpty()) {
            batchUpdateDatabase(batch);
        }
    }
}
@Component
public class WriteBehindPattern {
    
    private final Queue<User> writeQueue = new ConcurrentLinkedQueue<>();
    
    public void updateUser(User user) {
        // 1. 立即更新缓存
        updateCache(user);
        
        // 2. 异步更新数据库
        writeQueue.offer(user);
    }
    
    @Scheduled(fixedDelay = 1000)
    public void flushToDatabase() {
        List<User> batch = new ArrayList<>();
        User user;
        while ((user = writeQueue.poll()) != null && batch.size() < 100) {
            batch.add(user);
        }
        
        if (!batch.isEmpty()) {
            batchUpdateDatabase(batch);
        }
    }
}

分布式锁

简单分布式锁

java
@Component
public class RedisDistributedLock {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    public boolean tryLock(String lockKey, String requestId, long expireTime) {
        // 使用SET命令的NX和EX参数实现原子操作
        Boolean result = redisTemplate.opsForValue()
            .setIfAbsent(lockKey, requestId, Duration.ofMillis(expireTime));
        return Boolean.TRUE.equals(result);
    }
    
    public boolean releaseLock(String lockKey, String requestId) {
        // 使用Lua脚本保证原子性
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                       "return redis.call('del', KEYS[1]) " +
                       "else return 0 end";
        
        Long result = redisTemplate.execute(
            RedisScript.of(script, Long.class),
            Collections.singletonList(lockKey),
            requestId
        );
        
        return Long.valueOf(1).equals(result);
    }
}

// 使用示例
@Service
public class OrderService {
    
    @Autowired
    private RedisDistributedLock distributedLock;
    
    public void processOrder(String orderId) {
        String lockKey = "order_lock:" + orderId;
        String requestId = UUID.randomUUID().toString();
        
        if (distributedLock.tryLock(lockKey, requestId, 30000)) {
            try {
                // 处理订单逻辑
                doProcessOrder(orderId);
            } finally {
                distributedLock.releaseLock(lockKey, requestId);
            }
        } else {
            throw new RuntimeException("获取锁失败,订单正在处理中");
        }
    }
}
@Component
public class RedisDistributedLock {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    public boolean tryLock(String lockKey, String requestId, long expireTime) {
        // 使用SET命令的NX和EX参数实现原子操作
        Boolean result = redisTemplate.opsForValue()
            .setIfAbsent(lockKey, requestId, Duration.ofMillis(expireTime));
        return Boolean.TRUE.equals(result);
    }
    
    public boolean releaseLock(String lockKey, String requestId) {
        // 使用Lua脚本保证原子性
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                       "return redis.call('del', KEYS[1]) " +
                       "else return 0 end";
        
        Long result = redisTemplate.execute(
            RedisScript.of(script, Long.class),
            Collections.singletonList(lockKey),
            requestId
        );
        
        return Long.valueOf(1).equals(result);
    }
}

// 使用示例
@Service
public class OrderService {
    
    @Autowired
    private RedisDistributedLock distributedLock;
    
    public void processOrder(String orderId) {
        String lockKey = "order_lock:" + orderId;
        String requestId = UUID.randomUUID().toString();
        
        if (distributedLock.tryLock(lockKey, requestId, 30000)) {
            try {
                // 处理订单逻辑
                doProcessOrder(orderId);
            } finally {
                distributedLock.releaseLock(lockKey, requestId);
            }
        } else {
            throw new RuntimeException("获取锁失败,订单正在处理中");
        }
    }
}

可重入分布式锁

java
@Component
public class ReentrantRedisLock {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    private final ThreadLocal<Map<String, Integer>> lockCount = new ThreadLocal<>();
    
    public boolean lock(String lockKey, long expireTime) {
        String threadId = Thread.currentThread().getId() + "";
        
        // 检查当前线程是否已持有锁
        Map<String, Integer> counts = lockCount.get();
        if (counts != null && counts.containsKey(lockKey)) {
            counts.put(lockKey, counts.get(lockKey) + 1);
            return true;
        }
        
        // 尝试获取锁
        Boolean acquired = redisTemplate.opsForValue()
            .setIfAbsent(lockKey, threadId, Duration.ofMillis(expireTime));
            
        if (Boolean.TRUE.equals(acquired)) {
            if (counts == null) {
                counts = new HashMap<>();
                lockCount.set(counts);
            }
            counts.put(lockKey, 1);
            return true;
        }
        
        return false;
    }
    
    public void unlock(String lockKey) {
        Map<String, Integer> counts = lockCount.get();
        if (counts == null || !counts.containsKey(lockKey)) {
            return;
        }
        
        int count = counts.get(lockKey);
        if (count > 1) {
            counts.put(lockKey, count - 1);
        } else {
            counts.remove(lockKey);
            if (counts.isEmpty()) {
                lockCount.remove();
            }
            
            // 释放Redis锁
            String threadId = Thread.currentThread().getId() + "";
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                           "return redis.call('del', KEYS[1]) " +
                           "else return 0 end";
            
            redisTemplate.execute(
                RedisScript.of(script, Long.class),
                Collections.singletonList(lockKey),
                threadId
            );
        }
    }
}
@Component
public class ReentrantRedisLock {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    private final ThreadLocal<Map<String, Integer>> lockCount = new ThreadLocal<>();
    
    public boolean lock(String lockKey, long expireTime) {
        String threadId = Thread.currentThread().getId() + "";
        
        // 检查当前线程是否已持有锁
        Map<String, Integer> counts = lockCount.get();
        if (counts != null && counts.containsKey(lockKey)) {
            counts.put(lockKey, counts.get(lockKey) + 1);
            return true;
        }
        
        // 尝试获取锁
        Boolean acquired = redisTemplate.opsForValue()
            .setIfAbsent(lockKey, threadId, Duration.ofMillis(expireTime));
            
        if (Boolean.TRUE.equals(acquired)) {
            if (counts == null) {
                counts = new HashMap<>();
                lockCount.set(counts);
            }
            counts.put(lockKey, 1);
            return true;
        }
        
        return false;
    }
    
    public void unlock(String lockKey) {
        Map<String, Integer> counts = lockCount.get();
        if (counts == null || !counts.containsKey(lockKey)) {
            return;
        }
        
        int count = counts.get(lockKey);
        if (count > 1) {
            counts.put(lockKey, count - 1);
        } else {
            counts.remove(lockKey);
            if (counts.isEmpty()) {
                lockCount.remove();
            }
            
            // 释放Redis锁
            String threadId = Thread.currentThread().getId() + "";
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                           "return redis.call('del', KEYS[1]) " +
                           "else return 0 end";
            
            redisTemplate.execute(
                RedisScript.of(script, Long.class),
                Collections.singletonList(lockKey),
                threadId
            );
        }
    }
}

消息队列

简单消息队列

java
@Component
public class RedisMessageQueue {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    // 生产者
    public void sendMessage(String queue, String message) {
        redisTemplate.opsForList().leftPush(queue, message);
    }
    
    // 消费者(阻塞)
    public String receiveMessage(String queue, long timeout) {
        List<String> result = redisTemplate.opsForList()
            .rightPop(queue, Duration.ofSeconds(timeout));
        return result != null && !result.isEmpty() ? result.get(0) : null;
    }
    
    // 消费者(非阻塞)
    public String receiveMessageNonBlocking(String queue) {
        return redisTemplate.opsForList().rightPop(queue);
    }
}
@Component
public class RedisMessageQueue {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    // 生产者
    public void sendMessage(String queue, String message) {
        redisTemplate.opsForList().leftPush(queue, message);
    }
    
    // 消费者(阻塞)
    public String receiveMessage(String queue, long timeout) {
        List<String> result = redisTemplate.opsForList()
            .rightPop(queue, Duration.ofSeconds(timeout));
        return result != null && !result.isEmpty() ? result.get(0) : null;
    }
    
    // 消费者(非阻塞)
    public String receiveMessageNonBlocking(String queue) {
        return redisTemplate.opsForList().rightPop(queue);
    }
}

延迟队列

java
@Component
public class RedisDelayQueue {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    // 添加延迟消息
    public void addDelayMessage(String queue, String message, long delaySeconds) {
        long executeTime = System.currentTimeMillis() + delaySeconds * 1000;
        redisTemplate.opsForZSet().add(queue, message, executeTime);
    }
    
    // 获取到期消息
    @Scheduled(fixedDelay = 1000)
    public void processDelayMessages() {
        String queue = "delay_queue";
        long currentTime = System.currentTimeMillis();
        
        Set<String> messages = redisTemplate.opsForZSet()
            .rangeByScore(queue, 0, currentTime);
            
        for (String message : messages) {
            // 处理消息
            processMessage(message);
            
            // 删除已处理的消息
            redisTemplate.opsForZSet().remove(queue, message);
        }
    }
}
@Component
public class RedisDelayQueue {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    // 添加延迟消息
    public void addDelayMessage(String queue, String message, long delaySeconds) {
        long executeTime = System.currentTimeMillis() + delaySeconds * 1000;
        redisTemplate.opsForZSet().add(queue, message, executeTime);
    }
    
    // 获取到期消息
    @Scheduled(fixedDelay = 1000)
    public void processDelayMessages() {
        String queue = "delay_queue";
        long currentTime = System.currentTimeMillis();
        
        Set<String> messages = redisTemplate.opsForZSet()
            .rangeByScore(queue, 0, currentTime);
            
        for (String message : messages) {
            // 处理消息
            processMessage(message);
            
            // 删除已处理的消息
            redisTemplate.opsForZSet().remove(queue, message);
        }
    }
}

发布订阅

java
@Component
public class RedisPublisher {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    public void publishMessage(String channel, String message) {
        redisTemplate.convertAndSend(channel, message);
    }
}

@Component
public class RedisSubscriber {
    
    @RedisMessageListener(channels = "user_events")
    public void handleUserEvent(String message) {
        System.out.println("Received user event: " + message);
        // 处理用户事件
    }
    
    @RedisMessageListener(patterns = "order_*")
    public void handleOrderEvent(String message, String channel) {
        System.out.println("Received order event on " + channel + ": " + message);
        // 处理订单事件
    }
}
@Component
public class RedisPublisher {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    public void publishMessage(String channel, String message) {
        redisTemplate.convertAndSend(channel, message);
    }
}

@Component
public class RedisSubscriber {
    
    @RedisMessageListener(channels = "user_events")
    public void handleUserEvent(String message) {
        System.out.println("Received user event: " + message);
        // 处理用户事件
    }
    
    @RedisMessageListener(patterns = "order_*")
    public void handleOrderEvent(String message, String channel) {
        System.out.println("Received order event on " + channel + ": " + message);
        // 处理订单事件
    }
}

计数器和统计

访问计数

java
@Component
public class RedisCounter {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    // 增加访问次数
    public long incrementPageView(String pageId) {
        String key = "page_views:" + pageId;
        return redisTemplate.opsForValue().increment(key);
    }
    
    // 获取访问次数
    public long getPageViews(String pageId) {
        String key = "page_views:" + pageId;
        String count = redisTemplate.opsForValue().get(key);
        return count != null ? Long.parseLong(count) : 0;
    }
    
    // 每日访问统计
    public long incrementDailyPageView(String pageId) {
        String today = LocalDate.now().toString();
        String key = "daily_page_views:" + today + ":" + pageId;
        
        Long count = redisTemplate.opsForValue().increment(key);
        
        // 设置过期时间(保留30天)
        redisTemplate.expire(key, Duration.ofDays(30));
        
        return count;
    }
}
@Component
public class RedisCounter {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    // 增加访问次数
    public long incrementPageView(String pageId) {
        String key = "page_views:" + pageId;
        return redisTemplate.opsForValue().increment(key);
    }
    
    // 获取访问次数
    public long getPageViews(String pageId) {
        String key = "page_views:" + pageId;
        String count = redisTemplate.opsForValue().get(key);
        return count != null ? Long.parseLong(count) : 0;
    }
    
    // 每日访问统计
    public long incrementDailyPageView(String pageId) {
        String today = LocalDate.now().toString();
        String key = "daily_page_views:" + today + ":" + pageId;
        
        Long count = redisTemplate.opsForValue().increment(key);
        
        // 设置过期时间(保留30天)
        redisTemplate.expire(key, Duration.ofDays(30));
        
        return count;
    }
}

限流器

java
@Component
public class RedisRateLimiter {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    // 固定窗口限流
    public boolean isAllowed(String key, int limit, Duration window) {
        String windowKey = key + ":" + (System.currentTimeMillis() / window.toMillis());
        
        Long count = redisTemplate.opsForValue().increment(windowKey);
        
        if (count == 1) {
            redisTemplate.expire(windowKey, window);
        }
        
        return count <= limit;
    }
    
    // 滑动窗口限流
    public boolean isAllowedSlidingWindow(String key, int limit, Duration window) {
        long now = System.currentTimeMillis();
        long windowStart = now - window.toMillis();
        
        // 删除窗口外的记录
        redisTemplate.opsForZSet().removeRangeByScore(key, 0, windowStart);
        
        // 检查当前窗口内的请求数
        Long count = redisTemplate.opsForZSet().count(key, windowStart, now);
        
        if (count < limit) {
            // 添加当前请求
            redisTemplate.opsForZSet().add(key, UUID.randomUUID().toString(), now);
            redisTemplate.expire(key, window);
            return true;
        }
        
        return false;
    }
}
@Component
public class RedisRateLimiter {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    // 固定窗口限流
    public boolean isAllowed(String key, int limit, Duration window) {
        String windowKey = key + ":" + (System.currentTimeMillis() / window.toMillis());
        
        Long count = redisTemplate.opsForValue().increment(windowKey);
        
        if (count == 1) {
            redisTemplate.expire(windowKey, window);
        }
        
        return count <= limit;
    }
    
    // 滑动窗口限流
    public boolean isAllowedSlidingWindow(String key, int limit, Duration window) {
        long now = System.currentTimeMillis();
        long windowStart = now - window.toMillis();
        
        // 删除窗口外的记录
        redisTemplate.opsForZSet().removeRangeByScore(key, 0, windowStart);
        
        // 检查当前窗口内的请求数
        Long count = redisTemplate.opsForZSet().count(key, windowStart, now);
        
        if (count < limit) {
            // 添加当前请求
            redisTemplate.opsForZSet().add(key, UUID.randomUUID().toString(), now);
            redisTemplate.expire(key, window);
            return true;
        }
        
        return false;
    }
}

会话存储

Session管理

java
@Component
public class RedisSessionManager {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    private static final Duration SESSION_TIMEOUT = Duration.ofMinutes(30);
    
    public void createSession(String sessionId, Map<String, Object> attributes) {
        String key = "session:" + sessionId;
        redisTemplate.opsForHash().putAll(key, attributes);
        redisTemplate.expire(key, SESSION_TIMEOUT);
    }
    
    public void setAttribute(String sessionId, String attribute, Object value) {
        String key = "session:" + sessionId;
        redisTemplate.opsForHash().put(key, attribute, value);
        redisTemplate.expire(key, SESSION_TIMEOUT);
    }
    
    public Object getAttribute(String sessionId, String attribute) {
        String key = "session:" + sessionId;
        return redisTemplate.opsForHash().get(key, attribute);
    }
    
    public void removeSession(String sessionId) {
        String key = "session:" + sessionId;
        redisTemplate.delete(key);
    }
    
    public boolean isSessionValid(String sessionId) {
        String key = "session:" + sessionId;
        return Boolean.TRUE.equals(redisTemplate.hasKey(key));
    }
}
@Component
public class RedisSessionManager {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    private static final Duration SESSION_TIMEOUT = Duration.ofMinutes(30);
    
    public void createSession(String sessionId, Map<String, Object> attributes) {
        String key = "session:" + sessionId;
        redisTemplate.opsForHash().putAll(key, attributes);
        redisTemplate.expire(key, SESSION_TIMEOUT);
    }
    
    public void setAttribute(String sessionId, String attribute, Object value) {
        String key = "session:" + sessionId;
        redisTemplate.opsForHash().put(key, attribute, value);
        redisTemplate.expire(key, SESSION_TIMEOUT);
    }
    
    public Object getAttribute(String sessionId, String attribute) {
        String key = "session:" + sessionId;
        return redisTemplate.opsForHash().get(key, attribute);
    }
    
    public void removeSession(String sessionId) {
        String key = "session:" + sessionId;
        redisTemplate.delete(key);
    }
    
    public boolean isSessionValid(String sessionId) {
        String key = "session:" + sessionId;
        return Boolean.TRUE.equals(redisTemplate.hasKey(key));
    }
}

单点登录(SSO)

java
@Component
public class RedisSSOManager {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    public String createSSOToken(String userId) {
        String token = UUID.randomUUID().toString();
        String key = "sso_token:" + token;
        
        // 存储token到用户ID的映射
        redisTemplate.opsForValue().set(key, userId, Duration.ofHours(8));
        
        // 存储用户ID到token的映射(用于踢出其他会话)
        String userKey = "user_token:" + userId;
        String oldToken = redisTemplate.opsForValue().get(userKey);
        if (oldToken != null) {
            redisTemplate.delete("sso_token:" + oldToken);
        }
        redisTemplate.opsForValue().set(userKey, token, Duration.ofHours(8));
        
        return token;
    }
    
    public String validateToken(String token) {
        String key = "sso_token:" + token;
        return redisTemplate.opsForValue().get(key);
    }
    
    public void logout(String token) {
        String key = "sso_token:" + token;
        String userId = redisTemplate.opsForValue().get(key);
        
        if (userId != null) {
            redisTemplate.delete(key);
            redisTemplate.delete("user_token:" + userId);
        }
    }
}
@Component
public class RedisSSOManager {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    public String createSSOToken(String userId) {
        String token = UUID.randomUUID().toString();
        String key = "sso_token:" + token;
        
        // 存储token到用户ID的映射
        redisTemplate.opsForValue().set(key, userId, Duration.ofHours(8));
        
        // 存储用户ID到token的映射(用于踢出其他会话)
        String userKey = "user_token:" + userId;
        String oldToken = redisTemplate.opsForValue().get(userKey);
        if (oldToken != null) {
            redisTemplate.delete("sso_token:" + oldToken);
        }
        redisTemplate.opsForValue().set(userKey, token, Duration.ofHours(8));
        
        return token;
    }
    
    public String validateToken(String token) {
        String key = "sso_token:" + token;
        return redisTemplate.opsForValue().get(key);
    }
    
    public void logout(String token) {
        String key = "sso_token:" + token;
        String userId = redisTemplate.opsForValue().get(key);
        
        if (userId != null) {
            redisTemplate.delete(key);
            redisTemplate.delete("user_token:" + userId);
        }
    }
}

排行榜和推荐

实时排行榜

java
@Component
public class RedisLeaderboard {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    // 更新用户分数
    public void updateScore(String leaderboard, String userId, double score) {
        redisTemplate.opsForZSet().add(leaderboard, userId, score);
    }
    
    // 增加用户分数
    public void incrementScore(String leaderboard, String userId, double increment) {
        redisTemplate.opsForZSet().incrementScore(leaderboard, userId, increment);
    }
    
    // 获取排行榜前N名
    public List<String> getTopN(String leaderboard, int n) {
        Set<String> topUsers = redisTemplate.opsForZSet()
            .reverseRange(leaderboard, 0, n - 1);
        return new ArrayList<>(topUsers);
    }
    
    // 获取用户排名
    public Long getUserRank(String leaderboard, String userId) {
        return redisTemplate.opsForZSet().reverseRank(leaderboard, userId);
    }
    
    // 获取用户分数
    public Double getUserScore(String leaderboard, String userId) {
        return redisTemplate.opsForZSet().score(leaderboard, userId);
    }
    
    // 获取分数范围内的用户
    public Set<String> getUsersByScoreRange(String leaderboard, double min, double max) {
        return redisTemplate.opsForZSet().rangeByScore(leaderboard, min, max);
    }
}
@Component
public class RedisLeaderboard {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    // 更新用户分数
    public void updateScore(String leaderboard, String userId, double score) {
        redisTemplate.opsForZSet().add(leaderboard, userId, score);
    }
    
    // 增加用户分数
    public void incrementScore(String leaderboard, String userId, double increment) {
        redisTemplate.opsForZSet().incrementScore(leaderboard, userId, increment);
    }
    
    // 获取排行榜前N名
    public List<String> getTopN(String leaderboard, int n) {
        Set<String> topUsers = redisTemplate.opsForZSet()
            .reverseRange(leaderboard, 0, n - 1);
        return new ArrayList<>(topUsers);
    }
    
    // 获取用户排名
    public Long getUserRank(String leaderboard, String userId) {
        return redisTemplate.opsForZSet().reverseRank(leaderboard, userId);
    }
    
    // 获取用户分数
    public Double getUserScore(String leaderboard, String userId) {
        return redisTemplate.opsForZSet().score(leaderboard, userId);
    }
    
    // 获取分数范围内的用户
    public Set<String> getUsersByScoreRange(String leaderboard, double min, double max) {
        return redisTemplate.opsForZSet().rangeByScore(leaderboard, min, max);
    }
}

地理位置应用

附近的人

java
@Component
public class RedisGeoService {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    // 添加用户位置
    public void addUserLocation(String userId, double longitude, double latitude) {
        redisTemplate.opsForGeo().add("user_locations", 
            new Point(longitude, latitude), userId);
    }
    
    // 查找附近的用户
    public List<String> findNearbyUsers(String userId, double radius) {
        // 先获取用户当前位置
        List<Point> positions = redisTemplate.opsForGeo()
            .position("user_locations", userId);
            
        if (positions.isEmpty()) {
            return Collections.emptyList();
        }
        
        Point userPosition = positions.get(0);
        
        // 查找半径内的用户
        GeoResults<RedisGeoCommands.GeoLocation<String>> results = 
            redisTemplate.opsForGeo().radius("user_locations", 
                new Circle(userPosition, new Distance(radius, Metrics.KILOMETERS)));
        
        return results.getContent().stream()
            .map(result -> result.getContent().getName())
            .filter(name -> !name.equals(userId)) // 排除自己
            .collect(Collectors.toList());
    }
    
    // 计算两用户间距离
    public Distance getDistanceBetweenUsers(String userId1, String userId2) {
        return redisTemplate.opsForGeo()
            .distance("user_locations", userId1, userId2, Metrics.KILOMETERS);
    }
}
@Component
public class RedisGeoService {
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    // 添加用户位置
    public void addUserLocation(String userId, double longitude, double latitude) {
        redisTemplate.opsForGeo().add("user_locations", 
            new Point(longitude, latitude), userId);
    }
    
    // 查找附近的用户
    public List<String> findNearbyUsers(String userId, double radius) {
        // 先获取用户当前位置
        List<Point> positions = redisTemplate.opsForGeo()
            .position("user_locations", userId);
            
        if (positions.isEmpty()) {
            return Collections.emptyList();
        }
        
        Point userPosition = positions.get(0);
        
        // 查找半径内的用户
        GeoResults<RedisGeoCommands.GeoLocation<String>> results = 
            redisTemplate.opsForGeo().radius("user_locations", 
                new Circle(userPosition, new Distance(radius, Metrics.KILOMETERS)));
        
        return results.getContent().stream()
            .map(result -> result.getContent().getName())
            .filter(name -> !name.equals(userId)) // 排除自己
            .collect(Collectors.toList());
    }
    
    // 计算两用户间距离
    public Distance getDistanceBetweenUsers(String userId1, String userId2) {
        return redisTemplate.opsForGeo()
            .distance("user_locations", userId1, userId2, Metrics.KILOMETERS);
    }
}

最佳实践

1. 键命名规范

  • 使用有意义的前缀:user:1001:profile
  • 使用冒号分隔层级:cache:product:category:electronics
  • 避免过长的键名,影响内存使用

2. 过期时间设置

  • 根据业务需求设置合理的过期时间
  • 使用随机过期时间避免缓存雪崩
  • 定期清理无用的键

3. 内存优化

  • 选择合适的数据结构
  • 使用压缩配置减少内存占用
  • 监控内存使用情况

4. 性能优化

  • 使用pipeline批量操作
  • 避免大key,影响性能
  • 合理使用连接池

总结

Redis在现代应用中有着广泛的应用场景,从简单的缓存到复杂的分布式系统组件。正确理解和使用Redis的各种数据结构和特性,能够有效解决许多技术问题,提升系统的性能和可扩展性。在实际应用中,需要根据具体业务需求选择合适的使用模式,并注意性能优化和最佳实践。