返利软件的分布式缓存架构:Redis集群在高并发场景下的优化策略

返利软件的分布式缓存架构:Redis集群在高并发场景下的优化策略

大家好,我是阿可,微赚淘客系统及省赚客APP创始人,是个冬天不穿秋裤,天冷也要风度的程序猿!

在返利软件的业务场景中,商品列表查询、用户返利余额展示、热门活动数据等高频操作,若直接依赖数据库查询,会导致数据库压力剧增,甚至引发服务雪崩。基于此,我们采用Redis集群作为分布式缓存 ,通过"主从复制+哨兵+分片"架构,结合缓存预热、穿透防护、一致性保障等优化策略,将核心接口响应时间从300ms降至20ms以内,数据库查询压力减少70%。以下从集群架构设计、核心优化策略、代码实现三方面展开,附完整技术方案与代码示例。

一、返利软件Redis集群架构设计

1.1 集群拓扑结构

针对返利软件的高并发需求(如大促期间商品查询QPS达5000+),设计三层Redis集群架构:

  1. 数据分片层:采用Redis Cluster分片机制,将缓存数据按哈希槽(16384个)分布到3个主节点,每个主节点对应2个从节点,实现数据分布式存储与负载均衡;
  2. 高可用层:通过Redis Sentinel哨兵集群(3个哨兵节点)监控主节点状态,主节点故障时自动将从节点晋升为主节点,保障服务不中断;
  3. 客户端层:使用Spring Data Redis结合Redisson客户端,实现集群节点发现、故障自动重连与分布式锁功能。

1.2 核心业务缓存分类

根据返利软件的业务特性,将缓存分为三类,对应不同的过期策略与存储结构:

  • 高频读缓存:商品列表、活动规则(采用String/Hash结构,过期时间1小时,定期预热);
  • 实时性缓存:用户返利余额、订单状态(采用String结构,过期时间5分钟,更新时主动刷新);
  • 分布式锁缓存:库存扣减、并发下单(采用Redisson的RLock,自动释放锁机制)。

二、Redis集群核心优化策略与代码实现

2.1 缓存穿透防护:布隆过滤器+空值缓存

针对"查询不存在的商品ID"等穿透场景,通过布隆过滤器拦截无效请求,结合空值缓存避免重复穿透,代码如下:

java 复制代码
package cn.juwatech.rebate.cache.guard;

import cn.juwatech.rebate.service.ProductService;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 缓存穿透防护组件(布隆过滤器+空值缓存)
 */
@Component
public class CachePenetrationGuard {
    // 布隆过滤器:预计存储100万商品ID,误判率0.01
    private BloomFilter<String> productBloomFilter;
    // 空值缓存过期时间(5分钟)
    private static final long NULL_CACHE_TTL = 300;

    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private ProductService productService;

    // 项目启动时初始化布隆过滤器(加载所有有效商品ID)
    @PostConstruct
    public void initBloomFilter() {
        // 1. 从数据库查询所有有效商品ID
        List<String> validProductIds = productService.listAllValidProductIds();
        // 2. 初始化布隆过滤器
        productBloomFilter = BloomFilter.create(
                Funnels.stringFunnel(StandardCharsets.UTF_8),
                validProductIds.size(),
                0.01
        );
        // 3. 将商品ID加入布隆过滤器
        validProductIds.forEach(productId -> productBloomFilter.put(productId));
    }

    /**
     * 检查商品ID是否可能存在(拦截无效请求)
     */
    public boolean mightContainProductId(String productId) {
        return productBloomFilter.mightContain(productId);
    }

    /**
     * 缓存空值(避免重复穿透)
     */
    public void cacheNullValue(String key) {
        redisTemplate.opsForValue().set(key, "", NULL_CACHE_TTL, TimeUnit.SECONDS);
    }
}

2.2 缓存击穿防护:互斥锁+热点数据永不过期

针对"热点商品缓存过期瞬间大量请求穿透到数据库"的场景,通过Redis分布式锁保证同一时间只有一个请求更新缓存,代码如下:

java 复制代码
package cn.juwatech.rebate.cache.service;

import cn.juwatech.rebate.cache.guard.CachePenetrationGuard;
import cn.juwatech.rebate.dto.ProductDTO;
import cn.juwatech.rebate.service.ProductService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson.JSON;
import java.util.concurrent.TimeUnit;

/**
 * 商品缓存服务(含击穿防护)
 */
@Service
public class ProductCacheService {
    private static final String CACHE_KEY_PRODUCT = "rebate:product:%s";
    private static final String LOCK_KEY_PRODUCT = "rebate:lock:product:%s";
    // 普通商品缓存过期时间(1小时)
    private static final long CACHE_TTL = 3600;
    // 热点商品缓存过期时间(24小时,配合后台定时刷新实现"永不过期")
    private static final long HOT_CACHE_TTL = 86400;

    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private ProductService productService;
    @Autowired
    private CachePenetrationGuard penetrationGuard;

    /**
     * 获取商品缓存(含穿透、击穿防护)
     */
    public ProductDTO getProductCache(String productId) {
        String cacheKey = String.format(CACHE_KEY_PRODUCT, productId);
        String jsonData;

        // 1. 穿透防护:布隆过滤器拦截无效商品ID
        if (!penetrationGuard.mightContainProductId(productId)) {
            penetrationGuard.cacheNullValue(cacheKey);
            return null;
        }

        // 2. 查询缓存
        jsonData = redisTemplate.opsForValue().get(cacheKey);
        if (jsonData != null && !"".equals(jsonData)) {
            return JSON.parseObject(jsonData, ProductDTO.class);
        }

        // 3. 击穿防护:获取分布式锁,确保同一时间只有一个请求更新缓存
        RLock lock = redissonClient.getLock(String.format(LOCK_KEY_PRODUCT, productId));
        try {
            // 尝试获取锁(等待1秒,持有5秒)
            if (lock.tryLock(1, 5, TimeUnit.SECONDS)) {
                // 4. 再次查询缓存(避免锁等待期间其他请求已更新缓存)
                jsonData = redisTemplate.opsForValue().get(cacheKey);
                if (jsonData != null && !"".equals(jsonData)) {
                    return JSON.parseObject(jsonData, ProductDTO.class);
                }

                // 5. 缓存未命中,查询数据库
                ProductDTO product = productService.getProductById(productId);
                if (product == null) {
                    // 空值缓存,避免重复穿透
                    penetrationGuard.cacheNullValue(cacheKey);
                    return null;
                }

                // 6. 存入缓存(热点商品设置长过期时间,非热点商品正常过期)
                long ttl = isHotProduct(productId) ? HOT_CACHE_TTL : CACHE_TTL;
                redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(product), ttl, TimeUnit.SECONDS);

                return product;
            } else {
                // 未获取到锁,重试查询缓存(避免直接查库)
                Thread.sleep(100);
                return getProductCache(productId);
            }
        } catch (InterruptedException e) {
            throw new RuntimeException("获取商品缓存失败", e);
        } finally {
            // 释放锁(确保锁一定会释放)
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * 判断是否为热点商品(简化实现:如销量Top1000)
     */
    private boolean isHotProduct(String productId) {
        List<String> hotProductIds = productService.listHotProductIds(1000);
        return hotProductIds.contains(productId);
    }
}

2.3 缓存一致性保障:更新数据库后主动刷新缓存

针对"用户返利余额更新后缓存与数据库不一致"的场景,采用"更新数据库后主动删除旧缓存+延迟双删"策略,代码如下:

java 复制代码
package cn.juwatech.rebate.service.impl;

import cn.juwatech.rebate.cache.service.UserCacheService;
import cn.juwatech.rebate.entity.UserRebate;
import cn.juwatech.rebate.mapper.UserRebateMapper;
import cn.juwatech.rebate.service.UserRebateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.concurrent.TimeUnit;

/**
 * 用户返利服务实现(含缓存一致性保障)
 */
@Service
public class UserRebateServiceImpl implements UserRebateService {
    private static final String CACHE_KEY_USER_REBATE = "rebate:user:balance:%s";

    @Autowired
    private UserRebateMapper userRebateMapper;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private UserCacheService userCacheService;

    /**
     * 增加用户返利余额(更新数据库+主动刷新缓存)
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addRebateBalance(String userId, BigDecimal amount) {
        // 1. 更新数据库(本地事务)
        UserRebate userRebate = userRebateMapper.selectByUserId(userId);
        if (userRebate == null) {
            userRebate = new UserRebate();
            userRebate.setUserId(userId);
            userRebate.setBalance(amount);
            userRebateMapper.insert(userRebate);
        } else {
            userRebate.setBalance(userRebate.getBalance().add(amount));
            userRebateMapper.updateById(userRebate);
        }

        // 2. 主动删除旧缓存(避免脏读)
        String cacheKey = String.format(CACHE_KEY_USER_REBATE, userId);
        redisTemplate.delete(cacheKey);

        // 3. 延迟双删(应对极端场景:删除缓存后、更新数据库前的旧请求)
        delayDeleteCache(cacheKey, 100);

        // 4. 可选:主动刷新缓存(若后续查询频繁,避免缓存穿透)
        userCacheService.refreshUserRebateCache(userId);
    }

    /**
     * 延迟删除缓存(异步执行)
     */
    @Async
    public void delayDeleteCache(String cacheKey, long delayMillis) {
        try {
            Thread.sleep(delayMillis);
            redisTemplate.delete(cacheKey);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

2.4 Redis集群配置(Spring Boot)

通过Spring Boot配置Redis Cluster集群节点、连接池与序列化方式,代码如下:

yaml 复制代码
# application.yml 中Redis集群配置
spring:
  redis:
    # 集群节点(主从节点均配置,客户端自动识别主从)
    cluster:
      nodes:
        - redis-node1:6379
        - redis-node2:6379
        - redis-node3:6379
        - redis-node4:6379
        - redis-node5:6379
        - redis-node6:6379
      # 最大重定向次数(集群分片路由)
      max-redirects: 3
    # 连接池配置(lettuce连接池,性能优于jedis)
    lettuce:
      pool:
        max-active: 32  # 最大连接数
        max-idle: 16    # 最大空闲连接数
        min-idle: 8     # 最小空闲连接数
        max-wait: 3000  # 最大等待时间(毫秒)
    # 密码(若集群启用密码认证)
    password: RebateRedis@2024
    # 超时时间
    timeout: 5000

# Redisson配置(分布式锁、分布式集合等)
redisson:
  cluster-servers-config:
    node-addresses:
      - "redis://redis-node1:6379"
      - "redis://redis-node2:6379"
      - "redis://redis-node3:6379"
    password: RebateRedis@2024
    # 连接超时时间
    connect-timeout: 3000
    # 重试次数
    retry-attempts: 3
    # 重试间隔时间
    retry-interval: 1000

三、Redis集群运维与监控优化

  1. 内存碎片优化 :开启Redis的activedefrag(主动碎片整理),配置active-defrag-ignore-bytes 100mb,当碎片率超过10%时自动整理;
  2. 慢查询监控 :设置slowlog-log-slower-than 10000(记录超过10ms的命令),通过slowlog get分析慢查询,优化缓存Key设计与命令使用(如避免KEYS *);
  3. 集群扩容策略 :当单个主节点内存占用超过80%时,新增主从节点并通过redis-cli --cluster add-node加入集群,再执行reshard重新分配哈希槽;
  4. 数据备份 :开启Redis的RDB持久化(save 3600 1),结合AOF持久化(appendonly yes),确保数据在集群故障时可恢复。

本文著作权归聚娃科技省赚客app开发者团队,转载请注明出处!

相关推荐
在未来等你2 小时前
Kafka面试精讲 Day 18:磁盘IO与网络优化
大数据·分布式·面试·kafka·消息队列
lifallen2 小时前
字节跳动Redis变种Abase:无主多写架构如何解决高可用难题
数据结构·redis·分布式·算法·缓存
一水鉴天2 小时前
整体设计 之 绪 思维导图引擎 之 引 认知系统 之 引 认知系统 之 序 认知元架构 之6 拼句 之1 (豆包助手 之8)
架构·认知科学
梓仁沐白3 小时前
hadoop单机伪分布环境配置
大数据·hadoop·分布式
纪元A梦3 小时前
Redis最佳实践——安全与稳定性保障之高可用架构详解
redis·安全·架构
Dontla3 小时前
流行的前端架构与后端架构介绍(Architecture)
前端·架构
C++_girl4 小时前
缓存未命中
c++·缓存
虫小宝4 小时前
返利app排行榜的缓存更新策略:基于过期时间与主动更新的混合方案
java·spring·缓存
Light604 小时前
领码SPARK融合平台 · TS × Java 双向契约 —— 性能与治理篇|缓存分段与版本秩序
低代码·缓存·spark