跳转到内容
Go back

Redis缓存应用全面指南:从基础应用到问题解决的完整方案

Redis缓存应用全面指南:从基础应用到问题解决的完整方案

Redis缓存基础概念

什么是缓存?

缓存就像是我们生活中的便利店。想象一下,如果每次想买瓶水都要跑到几公里外的超市,那会非常麻烦。便利店就是把常用商品放在离我们更近的地方,方便快速获取。

在计算机系统中:

Redis作为缓存的优势

1. 高性能

数据库查询:平均100ms
Redis查询:  平均1ms
性能提升:   100倍

2. 丰富的数据结构

// String: 简单键值对缓存
redisTemplate.opsForValue().set("user:123", userJson);

// Hash: 对象属性缓存
redisTemplate.opsForHash().put("user:123", "name", "John");

// List: 列表数据缓存
redisTemplate.opsForList().rightPush("recent:articles", articleId);

// Zset: 排行榜缓存
redisTemplate.opsForZSet().add("leaderboard", userId, score);

3. 过期机制

// 自动过期,无需手动清理
redisTemplate.opsForValue().set("session:abc123", sessionData, Duration.ofMinutes(30));

Redis缓存应用场景

1. 数据库查询缓存

这是最常见的缓存应用场景,用于减少数据库查询压力。

用户信息缓存

@Service
public class UserCacheService {
    
    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    public User getUserById(Long userId) {
        String cacheKey = "user:" + userId;
        
        // 1. 先查缓存
        String userJson = redisTemplate.opsForValue().get(cacheKey);
        if (userJson != null) {
            return JSON.parseObject(userJson, User.class);
        }
        
        // 2. 缓存未命中,查数据库
        User user = userRepository.findById(userId);
        if (user != null) {
            // 3. 写入缓存,30分钟过期
            redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(user), 
                Duration.ofMinutes(30));
        }
        
        return user;
    }
    
    // 更新用户信息
    public void updateUser(User user) {
        // 1. 更新数据库
        userRepository.save(user);
        
        // 2. 删除缓存,下次查询时重新加载
        String cacheKey = "user:" + user.getId();
        redisTemplate.delete(cacheKey);
    }
}

商品信息缓存

@Service
public class ProductCacheService {
    
    // 商品详情缓存(长时间缓存)
    public Product getProductDetail(Long productId) {
        String cacheKey = "product:detail:" + productId;
        
        String productJson = redisTemplate.opsForValue().get(cacheKey);
        if (productJson != null) {
            return JSON.parseObject(productJson, Product.class);
        }
        
        Product product = productRepository.findById(productId);
        if (product != null) {
            // 商品信息变化不频繁,缓存1小时
            redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(product), 
                Duration.ofHours(1));
        }
        
        return product;
    }
    
    // 商品库存缓存(短时间缓存,数据变化频繁)
    public Integer getProductStock(Long productId) {
        String cacheKey = "product:stock:" + productId;
        
        String stockStr = redisTemplate.opsForValue().get(cacheKey);
        if (stockStr != null) {
            return Integer.valueOf(stockStr);
        }
        
        Integer stock = inventoryService.getStock(productId);
        if (stock != null) {
            // 库存变化频繁,只缓存30秒
            redisTemplate.opsForValue().set(cacheKey, stock.toString(), 
                Duration.ofSeconds(30));
        }
        
        return stock;
    }
}

2. 会话缓存

用于存储用户会话信息,支持分布式session。

@Component
public class SessionCacheService {
    
    // 保存用户会话
    public void saveSession(String sessionId, UserSession session) {
        String key = "session:" + sessionId;
        
        // 使用Hash结构存储会话的多个属性
        Map<String, String> sessionMap = new HashMap<>();
        sessionMap.put("userId", String.valueOf(session.getUserId()));
        sessionMap.put("username", session.getUsername());
        sessionMap.put("loginTime", String.valueOf(session.getLoginTime()));
        sessionMap.put("lastAccessTime", String.valueOf(System.currentTimeMillis()));
        
        redisTemplate.opsForHash().putAll(key, sessionMap);
        redisTemplate.expire(key, Duration.ofHours(2)); // 2小时过期
    }
    
    // 获取用户会话
    public UserSession getSession(String sessionId) {
        String key = "session:" + sessionId;
        
        Map<Object, Object> sessionMap = redisTemplate.opsForHash().entries(key);
        if (sessionMap.isEmpty()) {
            return null;
        }
        
        // 更新最后访问时间并续期
        redisTemplate.opsForHash().put(key, "lastAccessTime", 
            String.valueOf(System.currentTimeMillis()));
        redisTemplate.expire(key, Duration.ofHours(2));
        
        // 构建会话对象
        UserSession session = new UserSession();
        session.setUserId(Long.valueOf((String) sessionMap.get("userId")));
        session.setUsername((String) sessionMap.get("username"));
        session.setLoginTime(Long.valueOf((String) sessionMap.get("loginTime")));
        
        return session;
    }
    
    // 删除会话(用户登出)
    public void removeSession(String sessionId) {
        redisTemplate.delete("session:" + sessionId);
    }
}

3. 计算结果缓存

缓存复杂计算的结果,避免重复计算。

@Service
public class StatisticsCacheService {
    
    // 用户统计数据缓存
    public UserStatistics getUserStatistics(Long userId, String date) {
        String key = String.format("stats:user:%d:%s", userId, date);
        
        String statsJson = redisTemplate.opsForValue().get(key);
        if (statsJson != null) {
            return JSON.parseObject(statsJson, UserStatistics.class);
        }
        
        // 复杂的统计计算
        UserStatistics stats = calculateUserStatistics(userId, date);
        if (stats != null) {
            // 统计数据相对稳定,缓存6小时
            redisTemplate.opsForValue().set(key, JSON.toJSONString(stats), 
                Duration.ofHours(6));
        }
        
        return stats;
    }
    
    private UserStatistics calculateUserStatistics(Long userId, String date) {
        UserStatistics stats = new UserStatistics();
        // 复杂的数据库查询和计算
        stats.setPageViews(calculatePageViews(userId, date));
        stats.setOrderCount(calculateOrderCount(userId, date));
        stats.setTotalAmount(calculateTotalAmount(userId, date));
        return stats;
    }
}

4. 排行榜缓存

利用Redis的Sorted Set实现排行榜功能。

@Service
public class LeaderboardService {
    
    // 更新用户分数
    public void updateUserScore(Long userId, double score) {
        String key = "leaderboard:daily:" + LocalDate.now();
        redisTemplate.opsForZSet().add(key, "user:" + userId, score);
        
        // 设置排行榜过期时间
        redisTemplate.expire(key, Duration.ofDays(7));
    }
    
    // 获取排行榜前N名
    public List<UserRank> getTopRanking(int limit) {
        String key = "leaderboard:daily:" + LocalDate.now();
        
        Set<ZSetOperations.TypedTuple<String>> results = 
            redisTemplate.opsForZSet().reverseRangeWithScores(key, 0, limit - 1);
        
        return results.stream()
            .map(tuple -> new UserRank(tuple.getValue(), tuple.getScore()))
            .collect(Collectors.toList());
    }
    
    // 获取用户排名
    public Long getUserRank(Long userId) {
        String key = "leaderboard:daily:" + LocalDate.now();
        Long rank = redisTemplate.opsForZSet().reverseRank(key, "user:" + userId);
        return rank != null ? rank + 1 : null; // 排名从1开始
    }
}

5. 热点数据缓存

对于访问频率极高的数据,使用专门的热点缓存策略。

@Service
public class HotDataCacheService {
    
    // 热点商品缓存
    public List<Product> getHotProducts(String category) {
        String key = "hot:products:" + category;
        
        String productsJson = redisTemplate.opsForValue().get(key);
        if (productsJson != null) {
            return JSON.parseArray(productsJson, Product.class);
        }
        
        List<Product> hotProducts = productService.getHotProductsByCategory(category);
        if (!hotProducts.isEmpty()) {
            // 热点数据缓存时间短,但会预热刷新
            redisTemplate.opsForValue().set(key, JSON.toJSONString(hotProducts), 
                Duration.ofMinutes(10));
        }
        
        return hotProducts;
    }
    
    // 预热热点数据
    @Scheduled(fixedRate = 300000) // 每5分钟执行
    public void preheatHotData() {
        List<String> categories = Arrays.asList("electronics", "clothing", "books");
        
        categories.parallelStream().forEach(category -> {
            try {
                List<Product> hotProducts = productService.getHotProductsByCategory(category);
                String key = "hot:products:" + category;
                redisTemplate.opsForValue().set(key, JSON.toJSONString(hotProducts), 
                    Duration.ofMinutes(10));
            } catch (Exception e) {
                log.error("预热热点数据失败: {}", category, e);
            }
        });
    }
}

缓存常见问题及解决方案

1. 缓存穿透

问题定义:缓存中没有,数据库中也没有的数据被大量查询。

典型场景

// 恶意查询不存在的用户ID
for (int i = 0; i < 10000; i++) {
    getUserById(-i); // 负数ID不存在,每次都查数据库
}

解决方案一:布隆过滤器

@Component
public class BloomFilterService {
    
    private final BloomFilter<Long> userBloomFilter;
    
    public BloomFilterService() {
        // 创建布隆过滤器:预期100万元素,1%误判率
        this.userBloomFilter = BloomFilter.create(
            Funnels.longFunnel(), 1000000, 0.01);
        
        // 初始化:将所有存在的用户ID加入过滤器
        initUserBloomFilter();
    }
    
    public User getUserById(Long userId) {
        // 先检查布隆过滤器
        if (!userBloomFilter.mightContain(userId)) {
            // 肯定不存在,直接返回null
            return null;
        }
        
        // 可能存在,继续正常查询流程
        return queryUserWithCache(userId);
    }
    
    private void initUserBloomFilter() {
        List<Long> allUserIds = userRepository.findAllUserIds();
        allUserIds.forEach(userBloomFilter::put);
    }
}

解决方案二:缓存空值

public User getUserById(Long userId) {
    String cacheKey = "user:" + userId;
    String userJson = redisTemplate.opsForValue().get(cacheKey);
    
    // 检查是否是缓存的空值
    if ("NULL".equals(userJson)) {
        return null;
    }
    
    if (userJson != null) {
        return JSON.parseObject(userJson, User.class);
    }
    
    // 查询数据库
    User user = userRepository.findById(userId);
    if (user != null) {
        // 缓存正常数据
        redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(user), 
            Duration.ofMinutes(30));
    } else {
        // 缓存空值,但过期时间要短
        redisTemplate.opsForValue().set(cacheKey, "NULL", 
            Duration.ofMinutes(2));
    }
    
    return user;
}

2. 缓存击穿

问题定义:热点数据缓存过期,大量请求同时查询数据库。

解决方案:分布式锁

@Service
public class CacheBreakdownSolution {
    
    public Product getProductById(Long productId) {
        String cacheKey = "product:" + productId;
        String lockKey = "lock:product:" + productId;
        
        // 1. 查询缓存
        String productJson = redisTemplate.opsForValue().get(cacheKey);
        if (productJson != null) {
            return JSON.parseObject(productJson, Product.class);
        }
        
        // 2. 缓存未命中,尝试获取分布式锁
        String lockValue = UUID.randomUUID().toString();
        Boolean lockAcquired = redisTemplate.opsForValue()
            .setIfAbsent(lockKey, lockValue, Duration.ofSeconds(10));
        
        if (lockAcquired) {
            try {
                // 3. 获得锁,再次检查缓存
                productJson = redisTemplate.opsForValue().get(cacheKey);
                if (productJson != null) {
                    return JSON.parseObject(productJson, Product.class);
                }
                
                // 4. 查询数据库并缓存
                Product product = productRepository.findById(productId);
                if (product != null) {
                    redisTemplate.opsForValue().set(cacheKey, 
                        JSON.toJSONString(product), Duration.ofMinutes(30));
                }
                
                return product;
            } finally {
                // 5. 释放锁
                releaseLock(lockKey, lockValue);
            }
        } else {
            // 6. 未获得锁,等待后重试
            try {
                Thread.sleep(50);
                return getProductById(productId);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return null;
            }
        }
    }
    
    private void releaseLock(String lockKey, String lockValue) {
        String script = 
            "if redis.call('get', KEYS[1]) == ARGV[1] then " +
            "return redis.call('del', KEYS[1]) " +
            "else return 0 end";
        
        redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), 
            Collections.singletonList(lockKey), lockValue);
    }
}

3. 缓存雪崩

问题定义:大量缓存同时失效,请求都打到数据库。

解决方案一:随机过期时间

public void cacheUserWithRandomExpire(User user) {
    String cacheKey = "user:" + user.getId();
    
    // 基础过期时间30分钟,随机增加0-10分钟
    int baseExpireMinutes = 30;
    int randomMinutes = new Random().nextInt(10);
    int totalExpireMinutes = baseExpireMinutes + randomMinutes;
    
    redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(user), 
        Duration.ofMinutes(totalExpireMinutes));
}

解决方案二:多级缓存

@Component
public class MultiLevelCacheService {
    
    // L1缓存:本地缓存
    private final LoadingCache<String, String> localCache = Caffeine.newBuilder()
        .maximumSize(10000)
        .expireAfterWrite(Duration.ofMinutes(5))
        .build(key -> null);
    
    public Product getProduct(Long productId) {
        String cacheKey = "product:" + productId;
        
        // 1. 查询本地缓存
        String productJson = localCache.getIfPresent(cacheKey);
        if (productJson != null) {
            return JSON.parseObject(productJson, Product.class);
        }
        
        // 2. 查询Redis缓存
        productJson = redisTemplate.opsForValue().get(cacheKey);
        if (productJson != null) {
            localCache.put(cacheKey, productJson);
            return JSON.parseObject(productJson, Product.class);
        }
        
        // 3. 查询数据库
        Product product = productRepository.findById(productId);
        if (product != null) {
            productJson = JSON.toJSONString(product);
            
            // 写入两级缓存
            redisTemplate.opsForValue().set(cacheKey, productJson, 
                Duration.ofMinutes(30));
            localCache.put(cacheKey, productJson);
        }
        
        return product;
    }
}

4. 缓存更新策略

策略一:先更新数据库,再删除缓存

@Transactional
public void updateUser(User user) {
    // 1. 更新数据库
    userRepository.save(user);
    
    // 2. 删除缓存
    String cacheKey = "user:" + user.getId();
    redisTemplate.delete(cacheKey);
}

策略二:基于消息队列的异步更新

@Component
public class CacheUpdateService {
    
    @Autowired
    private RabbitTemplate rabbitTemplate;
    
    @Transactional
    public void updateUserData(User user) {
        // 1. 更新数据库
        userRepository.save(user);
        
        // 2. 发送缓存更新消息
        CacheUpdateMessage message = new CacheUpdateMessage();
        message.setKey("user:" + user.getId());
        message.setOperation("UPDATE");
        
        rabbitTemplate.convertAndSend("cache.update", message);
    }
    
    @RabbitListener(queues = "cache.update")
    public void handleCacheUpdate(CacheUpdateMessage message) {
        // 删除缓存,让下次查询时重新加载
        redisTemplate.delete(message.getKey());
    }
}

高级缓存应用

1. 分布式锁

@Component
public class RedisDistributedLock {
    
    public boolean tryLock(String lockKey, String lockValue, long expireTime) {
        Boolean result = redisTemplate.opsForValue()
            .setIfAbsent(lockKey, lockValue, Duration.ofSeconds(expireTime));
        return Boolean.TRUE.equals(result);
    }
    
    public void releaseLock(String lockKey, String lockValue) {
        String script = 
            "if redis.call('get', KEYS[1]) == ARGV[1] then " +
            "return redis.call('del', KEYS[1]) " +
            "else return 0 end";
        
        redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), 
            Collections.singletonList(lockKey), lockValue);
    }
    
    // 使用示例:防止重复提交
    @PostMapping("/order")
    public ResponseEntity<String> createOrder(@RequestBody OrderRequest request, 
                                             HttpServletRequest httpRequest) {
        String userId = getCurrentUserId();
        String lockKey = "order:create:" + userId;
        String lockValue = UUID.randomUUID().toString();
        
        if (tryLock(lockKey, lockValue, 10)) {
            try {
                // 创建订单的业务逻辑
                orderService.createOrder(request);
                return ResponseEntity.ok("订单创建成功");
            } finally {
                releaseLock(lockKey, lockValue);
            }
        } else {
            return ResponseEntity.badRequest().body("请勿重复提交");
        }
    }
}

2. 限流器

@Component
public class RateLimiter {
    
    // 滑动窗口限流
    public boolean isAllowed(String key, int limit, int windowSeconds) {
        long now = System.currentTimeMillis();
        long windowStart = now - windowSeconds * 1000L;
        
        String script = 
            "redis.call('zremrangebyscore', KEYS[1], 0, ARGV[1]) " +
            "local count = redis.call('zcard', KEYS[1]) " +
            "if count < tonumber(ARGV[2]) then " +
            "  redis.call('zadd', KEYS[1], ARGV[3], ARGV[3]) " +
            "  redis.call('expire', KEYS[1], ARGV[4]) " +
            "  return 1 " +
            "else " +
            "  return 0 " +
            "end";
        
        Long result = redisTemplate.execute(
            new DefaultRedisScript<>(script, Long.class),
            Collections.singletonList(key),
            String.valueOf(windowStart),
            String.valueOf(limit),
            String.valueOf(now),
            String.valueOf(windowSeconds)
        );
        
        return Long.valueOf(1).equals(result);
    }
}

// 使用示例
@RestController
public class ApiController {
    
    @GetMapping("/api/data")
    public ResponseEntity<Object> getData(HttpServletRequest request) {
        String clientIp = getClientIp(request);
        String rateLimitKey = "rate_limit:" + clientIp;
        
        // 每分钟最多100次请求
        if (!rateLimiter.isAllowed(rateLimitKey, 100, 60)) {
            return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS)
                .body("请求过于频繁,请稍后再试");
        }
        
        return ResponseEntity.ok(businessService.getData());
    }
}

缓存监控和运维

1. 缓存命中率监控

@Component
public class CacheMetrics {
    
    private final AtomicLong hitCount = new AtomicLong(0);
    private final AtomicLong missCount = new AtomicLong(0);
    
    public void recordHit() {
        hitCount.incrementAndGet();
    }
    
    public void recordMiss() {
        missCount.incrementAndGet();
    }
    
    public double getHitRate() {
        long hits = hitCount.get();
        long misses = missCount.get();
        long total = hits + misses;
        return total == 0 ? 0.0 : (double) hits / total;
    }
    
    // 定期输出监控指标
    @Scheduled(fixedRate = 60000) // 每分钟
    public void reportMetrics() {
        double hitRate = getHitRate();
        log.info("缓存命中率: {:.2f}%, 命中次数: {}, 未命中次数: {}", 
            hitRate * 100, hitCount.get(), missCount.get());
    }
}

2. 缓存健康检查

@Component
public class CacheHealthCheck {
    
    @EventListener
    @Async
    public void checkCacheHealth() {
        try {
            // 测试Redis连接
            String testKey = "health:check:" + System.currentTimeMillis();
            redisTemplate.opsForValue().set(testKey, "OK", Duration.ofSeconds(10));
            String result = redisTemplate.opsForValue().get(testKey);
            redisTemplate.delete(testKey);
            
            if ("OK".equals(result)) {
                log.info("缓存健康检查通过");
            } else {
                log.error("缓存健康检查失败:读写测试不通过");
                // 发送告警
                alertService.sendAlert("Redis缓存读写测试失败");
            }
        } catch (Exception e) {
            log.error("缓存健康检查异常", e);
            alertService.sendAlert("Redis缓存连接异常: " + e.getMessage());
        }
    }
}

3. 缓存清理策略

@Component
public class CacheCleanupService {
    
    @Scheduled(fixedDelay = 300000) // 每5分钟
    public void cleanupExpiredCache() {
        try {
            // 清理过期的临时缓存
            cleanupTempCache();
            
            // 清理过期的统计缓存
            cleanupStatisticsCache();
            
        } catch (Exception e) {
            log.error("缓存清理失败", e);
        }
    }
    
    private void cleanupTempCache() {
        Set<String> tempKeys = redisTemplate.keys("temp:*");
        if (tempKeys != null && !tempKeys.isEmpty()) {
            // 检查哪些key已经过期但还没被Redis清理
            for (String key : tempKeys) {
                Long ttl = redisTemplate.getExpire(key);
                if (ttl != null && ttl < 0) {
                    redisTemplate.delete(key);
                }
            }
        }
    }
    
    // 手动清理指定模式的缓存
    public int cleanupCacheByPattern(String pattern) {
        Set<String> keys = redisTemplate.keys(pattern);
        if (keys != null && !keys.isEmpty()) {
            Long deleted = redisTemplate.delete(keys);
            log.info("清理缓存完成,模式: {}, 清理数量: {}", pattern, deleted);
            return deleted.intValue();
        }
        return 0;
    }
}

缓存最佳实践

1. 缓存设计原则

选择合适的过期时间

// 根据数据特性设置不同的过期时间
public class CacheExpireStrategy {
    
    // 用户基本信息:变化不频繁,缓存时间长
    public static final Duration USER_INFO_EXPIRE = Duration.ofHours(2);
    
    // 商品库存:变化频繁,缓存时间短
    public static final Duration PRODUCT_STOCK_EXPIRE = Duration.ofSeconds(30);
    
    // 配置信息:很少变化,缓存时间很长
    public static final Duration CONFIG_EXPIRE = Duration.ofDays(1);
    
    // 统计数据:定时计算,缓存到下次计算时间
    public static final Duration STATISTICS_EXPIRE = Duration.ofHours(6);
}

合理的键命名规范

public class CacheKeyBuilder {
    
    private static final String SEPARATOR = ":";
    
    public static String userKey(Long userId) {
        return "user" + SEPARATOR + userId;
    }
    
    public static String userProfileKey(Long userId) {
        return "user" + SEPARATOR + "profile" + SEPARATOR + userId;
    }
    
    public static String productKey(Long productId) {
        return "product" + SEPARATOR + productId;
    }
    
    public static String sessionKey(String sessionId) {
        return "session" + SEPARATOR + sessionId;
    }
}

2. 性能优化建议

批量操作

// 批量获取用户信息
public Map<Long, User> batchGetUsers(List<Long> userIds) {
    // 1. 批量构建缓存key
    List<String> cacheKeys = userIds.stream()
        .map(id -> "user:" + id)
        .collect(Collectors.toList());
    
    // 2. 批量从Redis获取
    List<String> cachedValues = redisTemplate.opsForValue().multiGet(cacheKeys);
    
    Map<Long, User> result = new HashMap<>();
    List<Long> missedIds = new ArrayList<>();
    
    // 3. 处理缓存结果
    for (int i = 0; i < userIds.size(); i++) {
        String cachedValue = cachedValues.get(i);
        if (cachedValue != null) {
            result.put(userIds.get(i), JSON.parseObject(cachedValue, User.class));
        } else {
            missedIds.add(userIds.get(i));
        }
    }
    
    // 4. 批量查询未命中的数据
    if (!missedIds.isEmpty()) {
        List<User> users = userRepository.findByIdIn(missedIds);
        
        // 5. 批量写入缓存
        Map<String, String> cacheData = new HashMap<>();
        for (User user : users) {
            cacheData.put("user:" + user.getId(), JSON.toJSONString(user));
            result.put(user.getId(), user);
        }
        
        if (!cacheData.isEmpty()) {
            redisTemplate.opsForValue().multiSet(cacheData);
            // 批量设置过期时间
            cacheData.keySet().forEach(key -> 
                redisTemplate.expire(key, Duration.ofMinutes(30)));
        }
    }
    
    return result;
}

Pipeline使用

public void batchUpdateUserScores(Map<Long, Double> userScores) {
    redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
        for (Map.Entry<Long, Double> entry : userScores.entrySet()) {
            String key = "leaderboard:daily";
            String member = "user:" + entry.getKey();
            connection.zAdd(key.getBytes(), entry.getValue(), member.getBytes());
        }
        return null;
    });
}

总结

Redis作为缓存的应用是现代高性能系统的核心组件:

核心应用场景

关键问题解决

最佳实践要点

通过合理应用Redis缓存,可以显著提升系统性能,改善用户体验,为高并发业务提供强有力的支撑。


Share this post on:

Previous Post
Redis集群部署完全指南:从主从复制到分布式集群的全面解析
Next Post
Redis缓存三大经典问题:穿透、击穿、雪崩的深度解析与解决方案