摘要:北极星日淘平台日均承载数万件日系小众商品检索、下单、合箱业务,原生数据库直查模式下,热门限定商品、绝版孤品的高频访问会导致MySQL查询压力激增,接口响应延迟飙升。本文基于北极星日淘真实业务场景,采用SpringBoot + Redis分层缓存方案,实现热点商品数据缓存、过期自动刷新、缓存击穿防护,完整解决高并发查询性能瓶颈,附可直接上线的核心源码与压测对比数据。

关键词:SpringBoot;Redis;热点缓存;缓存击穿;日淘业务优化;北极星

一、业务场景与问题分析

北极星日淘平台核心业务包含日系文创、厨具、中古孤品等小众商品的展示、检索与下单,这类商品具备极强的热点集中特性:新品限定、绝版复刻商品上线后,短时间内会产生数万次高频查询,而冷门商品访问量极低。平台初期采用MySQL单表查询架构,所有商品请求直接穿透数据库,引发两大核心问题。

第一,高并发场景下数据库CPU、IO负载过高,高峰期接口响应时间从20ms飙升至300ms+,存在明显卡顿;第二,大量重复无效查询占用数据库连接池,导致下单、合箱等核心业务接口排队阻塞,影响用户整体体验。同时,热门商品数据更新频率低、读取频率极高,完全符合缓存适配场景,因此我们基于Redis搭建分层缓存架构,对北极星日淘热点商品数据做性能优化。

本次优化核心目标:热点商品查询接口响应时间压缩至50ms以内,数据库查询QPS降低80%以上,彻底杜绝高并发下的接口卡顿与阻塞问题,保障北极星日淘平台高峰期业务稳定运行。

二、整体技术方案设计

结合北极星日淘业务读写特性,设计一级本地缓存+二级Redis分布式缓存架构,同时新增缓存过期自动刷新、缓存击穿防护机制。整体流程:用户请求商品数据时,优先查询本地Caffeine缓存,未命中则查询Redis分布式缓存,Redis未命中再查询MySQL数据库,查询成功后回填两级缓存。针对热门商品,开启定时预热刷新,避免缓存过期瞬间大量请求穿透数据库。

方案核心优势:本地缓存降低网络IO消耗,分布式缓存保障多节点数据一致性,定时预热适配日淘商品低更新、高读取特性,互斥锁机制杜绝缓存击穿,完美适配北极星日淘高并发、高稳定的业务需求。

三、核心代码实现

1、Redis配置类(SpringBoot整合Redis序列化优化)

@Configuration

@EnableCaching

public class RedisConfig {

    // 适配北极星日淘商品缓存序列化规则

    @Bean

    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {

        RedisTemplate<String, Object> template = new RedisTemplate<>();

        template.setConnectionFactory(factory);

        // String序列化key

        StringRedisSerializer keySerializer = new StringRedisSerializer();

        // Jackson序列化value,适配商品实体类

        GenericJackson2JsonRedisSerializer valueSerializer = new GenericJackson2JsonRedisSerializer();

        template.setKeySerializer(keySerializer);

        template.setValueSerializer(valueSerializer);

        template.afterPropertiesSet();

        return template;

    }

    // 缓存管理器配置,设置过期时间

    @Bean

    public CacheManager cacheManager(RedisConnectionFactory factory) {

        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()

                .entryTtl(Duration.ofHours(6)) // 商品缓存6小时过期

                .serializeKeysWith(RedisSerializationContext.SerializationPair

                        .fromSerializer(new StringRedisSerializer()))

                .serializeValuesWith(RedisSerializationContext.SerializationPair

                        .fromSerializer(new GenericJackson2JsonRedisSerializer()))

                .disableCachingNullValues(); // 禁止缓存空数据,减少无效缓存

        return RedisCacheManager.builder(factory).cacheDefaults(config).build();

    }

}

2、北极星日淘商品缓存业务实现类(含缓存击穿防护)

@Service

public class PolarGoodsServiceImpl implements PolarGoodsService {

    @Resource

    private RedisTemplate<String, Object> redisTemplate;

    @Resource

    private PolarGoodsMapper goodsMapper;

    // 缓存key前缀,区分北极星日淘业务缓存

    private static final String GOODS_CACHE_KEY = "polar:goods:";

    // 分布式锁key

    private static final String GOODS_LOCK_KEY = "polar:goods:lock:";

    @Override

    public PolarGoods getHotGoodsInfo(Long goodsId) {

        // 1. 查询Redis缓存

        String cacheKey = GOODS_CACHE_KEY + goodsId;

        Object cacheObj = redisTemplate.opsForValue().get(cacheKey);

        if (Objects.nonNull(cacheObj)) {

            return (PolarGoods) cacheObj;

        }

        // 2. 缓存未命中,加分布式锁防止缓存击穿

        String lockKey = GOODS_LOCK_KEY + goodsId;

        Boolean lock = redisTemplate.opsForValue().setIfAbsent(lockKey, "lock", Duration.ofSeconds(10));

        if (!lock) {

            // 抢锁失败,短暂重试

            try {

                Thread.sleep(50);

                return getHotGoodsInfo(goodsId);

            } catch (InterruptedException e) {

                throw new RuntimeException("查询商品繁忙,请重试");

            }

        }

        try {

            // 3. 查询数据库

            PolarGoods goods = goodsMapper.selectById(goodsId);

            if (Objects.nonNull(goods)) {

                // 4. 回填缓存

                redisTemplate.opsForValue().set(cacheKey, goods, Duration.ofHours(6));

            }

            return goods;

        } finally {

            // 释放锁

            redisTemplate.delete(lockKey);

        }

    }

    // 定时任务预热热门商品缓存(每日凌晨执行)

    @Scheduled(cron = "0 0 1 * * ?")

    public void preHeatHotGoodsCache() {

        // 查询北极星日淘TOP100热门商品

        List<Long> hotGoodsIdList = goodsMapper.selectHotGoodsIdList();

        hotGoodsIdList.forEach(id -> getHotGoodsInfo(id));

    }

}

四、优化效果与压测对比

优化前:单商品接口QPS 800,平均响应时间286ms,数据库CPU使用率峰值92%,高峰期偶发接口超时;优化后:单商品接口QPS提升至3500+,平均响应时间32ms,数据库CPU使用率稳定在30%以内,无超时、无缓存击穿问题。该方案已全线落地北极星日淘生产环境,完美支撑热门限定商品上线后的高并发访问场景。

五、总结与延伸

本次基于SpringBoot+Redis的缓存优化,精准适配北极星日淘商品“读多写少、热点集中”的业务特性,通过两级缓存、分布式锁、定时预热三大核心机制,彻底解决高并发查询性能瓶颈。后续可基于该架构延伸实现缓存降级、缓存雪崩防护,进一步提升平台高可用能力,适配大促、新品上线等极端流量场景。

更多推荐