Redis + Caffeine多级缓存电商场景深度解析

news2025/3/31 17:14:05

Redis + Caffeine多级缓存

  • Redis + Caffeine多级缓存电商场景深度解析
    • 一、实施目的
    • 二、具体实施
      • 2.1 架构设计
      • 2.2 组件配置
      • 2.3 核心代码实现
    • 三、实施效果
      • 3.1 性能指标对比
      • 3.2 业务指标改善
      • 3.3 系统稳定性
    • 四、关键策略
      • 4.1 缓存预热
      • 4.2 一致性保障
      • 4.3 监控配置
        • Prometheus监控指标
    • 电商多级缓存完整实现方案
      • 1. 基础配置
        • 1.1 Maven依赖
        • 1.2 配置文件
      • 2. 核心实现类
        • 2.1 缓存配置类
        • 2.2 商品服务实现
        • 2.3 库存服务实现
      • 3. 辅助组件
        • 3.1 缓存预热
        • 3.2 监控端点
      • 关键点说明
        • 1. 多级缓存流程
        • 2. 缓存策略
        • 3. 一致性保障
        • 4. 监控能力
        • 5. 性能优化

Redis + Caffeine多级缓存电商场景深度解析

一、实施目的

  1. 性能优化

    • 降低商品详情页访问延迟
    • 提升系统整体吞吐量
  2. 成本控制

    • 减少Redis集群规模
    • 降低数据库查询压力
  3. 稳定性保障

    • 应对秒杀等高并发场景
    • 实现故障自动降级

二、具体实施

2.1 架构设计

用户请求 → Nginx → 应用服务器(Caffeine) → Redis集群 → MySQL/分库分表

2.2 组件配置

Caffeine配置

spring.cache.caffeine.spec=maximumSize=50000,expireAfterWrite=60s,refreshAfterWrite=30s,recordStats

Redis集群配置

spring.redis.cluster.nodes=192.168.1.101:6379,192.168.1.102:6379
spring.redis.lettuce.pool.max-active=500
spring.redis.lettuce.pool.max-wait=2000

2.3 核心代码实现

商品查询服务

public ProductDetail getProductDetail(Long productId) {
    // 本地缓存查询
    ProductDetail detail = caffeineCache.get(productId, k -> {
        // Redis查询
        String redisKey = "pd:" + productId;
        ProductDetail value = redisTemplate.opsForValue().get(redisKey);
        if (value == null) {
            // 数据库查询
            value = productDao.findById(productId);
            // 异步写入Redis
            executor.execute(() -> {
                redisTemplate.opsForValue().set(
                    redisKey, 
                    value,
                    60 + ThreadLocalRandom.current().nextInt(30),
                    TimeUnit.MINUTES
                );
            });
        }
        return value;
    });
    return detail;
}

库存扣减服务

public boolean deductStock(Long productId, int num) {
    // 本地库存标记
    if (!localStockMark.tryLock(productId)) {
        return false;
    }
    
    try {
        // Redis原子扣减
        Long remain = redisTemplate.execute(
            new DefaultRedisScript<>(
                "local stock = tonumber(redis.call('GET', KEYS[1]))\n" +
                "if stock >= tonumber(ARGV[1]) then\n" +
                "    return redis.call('DECRBY', KEYS[1], ARGV[1])\n" +
                "else\n" +
                "    return -1\n" +
                "end",
                Long.class
            ),
            Collections.singletonList("stock:" + productId),
            String.valueOf(num)
        );
        
        if (remain >= 0) {
            // 异步记录库存变更
            mq.sendStockMessage(productId, num);
            return true;
        }
        return false;
    } finally {
        localStockMark.unlock(productId);
    }
}

三、实施效果

3.1 性能指标对比

指标单Redis架构多级缓存架构提升幅度
平均响应时间68ms9ms655%
峰值QPS12万85万608%
数据库查询量100%8%减少92%

3.2 业务指标改善

  1. 秒杀场景

    • 下单成功率从35%提升至89%
    • 超卖问题完全杜绝
  2. 常规场景

    • 商品详情页加载时间从1.2s→180ms
    • 服务器成本降低40%

3.3 系统稳定性

  1. Redis故障时:

    • 核心商品仍可提供服务
    • 系统存活时间从<1分钟延长至30分钟
  2. 大促期间:

    • 流量波动减少70%
    • CPU负载降低55%

四、关键策略

4.1 缓存预热

@Scheduled(cron = "0 0 3 * * ?")
public void dailyPreheat() {
    List<Long> hotItems = hotProductService.predictHotItems();
    hotItems.parallelStream().forEach(id -> {
        ProductDetail detail = productDao.getDetail(id);
        caffeineCache.put(id, detail);
        redisTemplate.opsForValue().set(
            "pd:" + id,
            detail,
            6, TimeUnit.HOURS
        );
    });
}

4.2 一致性保障

@Transactional
public void updateProduct(Product product) {
    // 1.删除缓存
    caffeineCache.invalidate(product.getId());
    redisTemplate.delete("pd:" + product.getId());
    
    // 2.更新数据库
    productDao.update(product);
    
    // 3.延迟双删
    executor.schedule(() -> {
        redisTemplate.delete("pd:" + product.getId());
    }, 1, TimeUnit.SECONDS);
}

4.3 监控配置

Prometheus监控指标
 name: cache_hit_rate
 expr: rate(caffeine_cache_hits_total[5m]) / (rate(caffeine_cache_hits_total[5m]) +  rate(caffeine_cache_misses_total[5m]))

 name: redis_latency
 expr: histogram_quantile(0.99, rate(redis_command_duration_seconds_bucket[1m]))

电商多级缓存完整实现方案

1. 基础配置

1.1 Maven依赖
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>com.github.ben-manes.caffeine</groupId>
        <artifactId>caffeine</artifactId>
        <version>3.1.8</version>
    </dependency>
</dependencies>
1.2 配置文件
spring:
  redis:
    host: 127.0.0.1
    port: 6379
    password: yourpassword
    lettuce:
      pool:
        max-active: 16
        max-wait: 1000ms
        max-idle: 8

caffeine:
  specs:
    productCache: maximumSize=10000,expireAfterWrite=60s,recordStats
    stockCache: maximumSize=5000,expireAfterWrite=10s

2. 核心实现类

2.1 缓存配置类
@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    public CacheManager cacheManager() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();
        cacheManager.registerCustomCache("products", 
            Caffeine.from(caffeineProperties().getSpec("productCache")).build());
        cacheManager.registerCustomCache("stocks",
            Caffeine.from(caffeineProperties().getSpec("stockCache")).build());
        return cacheManager;
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        return template;
    }
}
2.2 商品服务实现
@Service
public class ProductServiceImpl implements ProductService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Cacheable(cacheNames = "products", key = "#productId")
    @Override
    public ProductDetail getProductDetail(Long productId) {
        String redisKey = "product:" + productId;
        ProductDetail detail = (ProductDetail) redisTemplate.opsForValue().get(redisKey);
        
        if (detail == null) {
            detail = loadFromDB(productId);
            redisTemplate.opsForValue().set(
                redisKey, 
                detail,
                60 + ThreadLocalRandom.current().nextInt(30),
                TimeUnit.MINUTES
            );
        }
        return detail;
    }

    private ProductDetail loadFromDB(Long productId) {
        // 数据库查询实现
        return productRepository.findById(productId);
    }
}
2.3 库存服务实现
@Service
public class StockServiceImpl implements StockService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Cacheable(cacheNames = "stocks", key = "#productId")
    @Override
    public Integer getStock(Long productId) {
        String redisKey = "stock:" + productId;
        Integer stock = (Integer) redisTemplate.opsForValue().get(redisKey);
        
        if (stock == null) {
            stock = loadStockFromDB(productId);
            redisTemplate.opsForValue().set(
                redisKey,
                stock,
                10, TimeUnit.SECONDS
            );
        }
        return stock;
    }

    @CacheEvict(cacheNames = "stocks", key = "#productId")
    @Override
    public boolean deductStock(Long productId, int quantity) {
        // 库存扣减逻辑
        return stockRepository.deductStock(productId, quantity);
    }
}

3. 辅助组件

3.1 缓存预热
@Component
public class CacheWarmUp implements CommandLineRunner {

    @Autowired
    private ProductService productService;
    
    @Override
    public void run(String... args) {
        List<Long> hotProducts = Arrays.asList(1001L, 1002L, 1003L);
        hotProducts.parallelStream().forEach(productService::getProductDetail);
    }
}
3.2 监控端点
@RestController
@RequestMapping("/cache")
public class CacheMonitorController {

    @Autowired
    private CacheManager cacheManager;
    
    @GetMapping("/stats")
    public Map<String, Object> getCacheStats() {
        Map<String, Object> stats = new HashMap<>();
        
        CaffeineCache productsCache = (CaffeineCache) cacheManager.getCache("products");
        if (productsCache != null) {
            stats.put("products", productsCache.getNativeCache().stats());
        }
        
        return stats;
    }
}

关键点说明

1. 多级缓存流程
  1. 先查Caffeine本地缓存
  2. 未命中则查Redis
  3. 仍未命中则查数据库
  4. 回填各级缓存
2. 缓存策略
数据类型本地缓存TTLRedis TTL
商品数据60秒30-90分钟(随机)
库存数据10秒10秒
3. 一致性保障
  1. 使用@CacheEvict保证更新时缓存失效
  2. 库存采用短过期时间自动刷新
4. 监控能力
  1. 通过/cache/stats端点暴露缓存命中率
  2. 集成Spring Boot Actuator
5. 性能优化
  1. 并行预热热点数据
  2. Redis连接池配置
  3. 本地缓存大小控制

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2323550.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Linux】应用层协议 HTTP

应用层协议 HTTP 一. HTTP 协议1. URL 地址2. urlencode 和 urldecode3. 请求与响应格式 二. HTTP 请求方法1. GET 和 POST (重点) 三. HTTP 状态码四. HTTP 常见报头五. 手写 HTTP 服务器 HTTP&#xff08;超文本传输协议&#xff09;是一种应用层协议&#xff0c;用于在万维网…

数据可视化TensorboardX和tensorBoard安装及使用

tensorBoard 和TensorboardX 安装及使用指南 tensorBoard 和 TensorBoardX 是用于可视化机器学习实验和模型训练过程的工具。TensorBoard 是 TensorFlow 官方提供的可视化工具&#xff0c;而 TensorBoardX 是其社区驱动的替代品&#xff0c;支持 PyTorch 等其他框架。以下是它…

【Hugging Face 开源库】Diffusers 库 —— 扩散模型

Diffusers 的三个主要组件1. DiffusionPipeline&#xff1a;端到端推理工具__call__ 函数callback_on_step_end 管道回调函数 2. 预训练模型架构和模块UNetVAE&#xff08;Variational AutoEncoder&#xff09;图像尺寸与 UNet 和 VAE 的关系EMA&#xff08;Exponential Moving…

AWTK-WEB 快速入门(6) - JS WebSocket 应用程序

WebSocket 可以实现双向通信&#xff0c;适合实时通信场景。本文介绍一下使用 Javacript 语言开发 AWTK-WEB 应用程序&#xff0c;并用 WebSocket 与服务器通讯。 用 AWTK Designer 新建一个应用程序 先安装 AWTK Designer&#xff1a; https://awtk.zlg.cn/web/index.html …

使用VSCODE导致CPU占用率过高的处理方法

1&#xff1a;cpptools 原因&#xff1a;原因是C/C会在全局搜索文件&#xff0c;可以快速进行跳转&#xff1b;当打开的文件过大&#xff0c;全局搜索文件会占用大量CPU&#xff1b; 处理方法&#xff1a; 1&#xff1a;每次只打开小文件夹&#xff1b; 2&#xff1a;打开大文…

【力扣hot100题】(004)盛水最多的容器

现在能这么快做出来纯粹是因为当时做的时候给我的印象实在太深了。 犹记得这题是当年开启我用CSDN记录leetcode日记历史的开端。 总之印象太深了不会都不行啊&#xff01;&#xff01;记得当年是想到用各种动态规划回溯等等等等最终发现是最简单贪心和双指针。 解法也是非常简…

用Deepseek写扫雷uniapp小游戏

扫雷作为Windows系统自带的经典小游戏&#xff0c;承载了许多人的童年回忆。本文将详细介绍如何使用Uniapp框架从零开始实现一个完整的扫雷游戏&#xff0c;包含核心算法、交互设计和状态管理。无论你是Uniapp初学者还是有一定经验的开发者&#xff0c;都能从本文中获得启发。 …

Eclipse IDE for ModusToolbox™ 3.4环境通过JLINK调试CYT4BB

使用JLINK在Eclipse IDE for ModusToolbox™ 3.4环境下调试CYT4BB&#xff0c;配置是难点。总结一下在IDE中配置JLINK调试中遇到的坑&#xff0c;以及如何一步一步解决遇到的问题。 1. JFLASH能够正常下载程序 首先要保证通过JFLASH(我使用的J-Flash V7.88c版本)能够通过JLIN…

修改git在提交代码时的名称

在git中&#xff0c;如果想修改提交代码作者的名字&#xff0c;可以进行以下操作&#xff1a; 1.在桌面或者文件夹内右击鼠标&#xff0c;点开Git Bash here。 2.进入后&#xff0c;通过git config user.name 回车查看当前名称。 3.通过git config --global user.name "…

【Linux】深入解析Linux命名管道(FIFO):原理、实现与实战应用

本文承接上文匿名管道&#xff1a;【Linux】深度解析Linux进程间通信&#xff1a;匿名管道原理、实战与高频问题排查-CSDN博客 深入探讨Linux进程间通信&#xff08;IPC&#xff09;&#xff0c;以匿名管道为核心&#xff0c;详细阐述其通信目的、实现前提及机制。涵盖数据传输…

第十四届蓝桥杯省赛电子类单片机学习记录(客观题)

01.一个8位的DAC转换器&#xff0c;供电电压为3.3V&#xff0c;参考电压2.4V&#xff0c;其ILSB产生的输出电压增量是&#xff08;D&#xff09;V。 A. 0.0129 B. 0.0047 C. 0.0064 D. 0.0094 解析&#xff1a; ILSB&#xff08;最低有效位&#xff09;的电压增量计算公式…

vim的一般操作(分屏操作) 和 Makefile 和 gdb

目录 一. vim的基本概念 二. vim基础操作 2.1 插入模式 aio 2.2 [插入模式]切换至[正常模式] Esc 2.3[正常模式]切换至[末行模式] shift ; 2.4 替换模式 Shift R 2.5 视图&#xff08;可视&#xff09;模式 (可以快速 删除//注释 或者 增加//注释) ctrl v 三&…

Apache Shiro 统一化实现多端登录(PC端移动端)

Apache Shiro 是一个强大且易用的Java安全框架&#xff0c;提供了身份验证、授权、密码学和会话管理等功能。它被广泛用于保护各种类型的应用程序&#xff0c;包括Web应用、桌面应用、RESTful服务、移动端应用和大型企业级应用。 需求背景 在当今数字化浪潮的推动下&#xff…

NAT—地址转换(实战篇)

一、实验拓扑&#xff1a; 二、实验需求&#xff1a; 1.实现内网主机访问外网 2.实现外网客户端能够访问内网服务器 三、实验思路 1.配置NAT地址池实现内网地址转换成公网地址&#xff0c;实现内网主机能够访问外网。 2.配置NAT Sever实现公网地址映射内网服务器地址&…

用HTML和CSS生成炫光动画卡片

这个效果结合了渐变、旋转和悬浮效果的炫酷动画示例&#xff0c;使用HTML和CSS实现。 一、效果 二、实现 代码如下&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport&quo…

FPGA_YOLO(三)

上一篇讲的是完全映射&#xff0c;也就是block中的所包含的所有的卷积以及归一&#xff0c;池化卷积 举例总共6个等都在pl侧进行处理&#xff08;写一个top 顶层 里面conv 1 bn1 relu1 pool1 conv1*1 conv 2 bn2 relu2 pool2 conv1*1 ....总共6个 &#xff09;&#xff0c;…

旅游CMS选型:WordPress、Joomla与Drupal对比

内容概要 在旅游行业数字化转型进程中&#xff0c;内容管理系统&#xff08;CMS&#xff09;的选择直接影响网站运营效率与用户体验。WordPress、Joomla和Drupal作为全球主流的开源CMS平台&#xff0c;其功能特性与行业适配性存在显著差异。本文将从旅游企业核心需求出发&…

全面适配iOS 18.4!通付盾加固产品全面升级,护航App安全上架

引言&#xff1a; 苹果官方新规落地&#xff01; 自2025年4月24日起&#xff0c;所有提交至App Store Connect的应用必须使用Xcode 16或更高版本构建&#xff0c;否则将面临审核驳回风险&#xff01;Beta版iOS 18.4、iPadOS 18.4现已推出&#xff0c;通付盾iOS加固产品率先完成…

一台电脑最多能接几个硬盘?

在使用电脑时&#xff0c;硬盘空间不够是许多用户都会遇到的问题。无论是摄影师、剪辑师等需要大量存储空间的专业人士&#xff0c;还是游戏玩家、数据备份爱好者&#xff0c;都可能希望通过增加硬盘来扩展存储容量。然而&#xff0c;一台电脑究竟最多能接多少个硬盘&#xff1…

【玩转全栈】---- Django 基于 Websocket 实现群聊(解决channel连接不了)

学习视频&#xff1a; 14-11 群聊&#xff08;一&#xff09;_哔哩哔哩_bilibili 目录 Websocket 连接不了&#xff1f; 收发数据 断开连接 完整代码 聊天室的实现 聊天室一 聊天室二 settings 配置 consumer 配置 多聊天室 Websocket 连接不了&#xff1f; 基于这篇博客&…