返利app排行榜的缓存更新策略:基于过期时间与主动更新的混合方案

返利app排行榜的缓存更新策略:基于过期时间与主动更新的混合方案

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

在返利APP中,“热门商品排行榜”“用户月度返利榜”是核心流量入口,这类数据的访问频率极高(日均百万次请求),但数据更新存在明确规律——商品销量、返利金额每小时统计一次,用户返利榜每日凌晨结算。若采用“实时查库”方案,单表百万级数据排序会导致接口响应时间超800ms;若仅依赖“过期时间缓存”,则可能出现数据滞后(如商品返利比例调整后,排行榜6小时未更新)。基于“过期时间+主动更新”的混合缓存策略,既能保证99.5%以上的缓存命中率,又能将数据一致性延迟控制在5分钟内,本文结合返利APP实际业务,提供完整技术实现方案。
返利app排行榜

一、混合缓存策略的核心设计:分层控制数据一致性

混合策略将排行榜缓存分为“基础缓存层”与“更新触发层”:

  • 基础缓存层:用Redis存储排行榜数据,设置合理过期时间(如热门商品榜1小时、用户返利榜24小时),应对高并发读请求;
  • 更新触发层:通过“定时任务预计算”“数据变更事件通知”两种方式,在缓存过期前主动更新数据,避免缓存击穿与数据滞后。

以“热门商品排行榜”为例,缓存Key设计规范:taoke:rank:goods:hot:{categoryId}(如taoke:rank:goods:hot:3C代表3C品类热门榜),Value采用ZSet结构(score为商品热度值,member为商品ID+返利信息JSON串)。

二、基础缓存层实现:Redis过期时间与缓存读写

2.1 缓存读取与过期时间配置

通过cn.juwatech.rank.service.GoodsRankService实现缓存优先读取,未命中时查库并更新缓存:

package cn.juwatech.rank.service;import cn.juwatech.cache.RedisService;
import cn.juwatech.goods.dto.GoodsRankDTO;
import cn.juwatech.goods.mapper.GoodsRankMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;@Service
public class GoodsRankService {@Autowiredprivate RedisService redisService;@Autowiredprivate GoodsRankMapper goodsRankMapper;// 热门商品榜缓存过期时间:1小时(3600秒)private static final long HOT_RANK_EXPIRE_SECONDS = 3600;// 缓存Key前缀private static final String HOT_RANK_KEY_PREFIX = "taoke:rank:goods:hot:";// 获取指定品类的热门商品榜public List<GoodsRankDTO> getHotGoodsRank(String categoryId, int topN) {String cacheKey = HOT_RANK_KEY_PREFIX + categoryId;// 1. 先从Redis获取缓存Set<String> cachedRank = redisService.zReverseRange(cacheKey, 0, topN - 1);if (cachedRank != null && !cachedRank.isEmpty()) {// 缓存命中:解析ZSet的member为GoodsRankDTOreturn cachedRank.stream().map(this::parseGoodsRankDTO).collect(Collectors.toList());}// 2. 缓存未命中:查库获取最新排行榜List<GoodsRankDTO> dbRank = goodsRankMapper.selectHotGoodsRank(categoryId, topN);// 3. 更新缓存(设置过期时间)if (dbRank != null && !dbRank.isEmpty()) {dbRank.forEach(rankDTO -> {// ZSet的score为商品热度值(销量*返利比例)double score = rankDTO.getSales() * rankDTO.getRebateRate();redisService.zAdd(cacheKey, rankDTO.toString(), score);});// 设置缓存过期时间redisService.expire(cacheKey, HOT_RANK_EXPIRE_SECONDS);}return dbRank;}// 解析ZSet的member字符串为GoodsRankDTO(简化实现,实际需用JSON工具)private GoodsRankDTO parseGoodsRankDTO(String memberStr) {String[] parts = memberStr.split("\\|");GoodsRankDTO dto = new GoodsRankDTO();dto.setGoodsId(parts[0]);dto.setGoodsName(parts[1]);dto.setRebateRate(Double.parseDouble(parts[2]));dto.setSales(Integer.parseInt(parts[3]));return dto;}
}

2.2 Redis工具类封装(cn.juwatech.cache.RedisService

package cn.juwatech.cache;import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Set;
import java.util.concurrent.TimeUnit;@Component
public class RedisService {@Resourceprivate RedisTemplate<String, Object> redisTemplate;// ZSet添加元素public Boolean zAdd(String key, Object value, double score) {return redisTemplate.opsForZSet().add(key, value, score);}// ZSet逆序获取指定范围元素(从高到低排序)public Set<String> zReverseRange(String key, long start, long end) {return redisTemplate.opsForZSet().reverseRange(key, start, end);}// 设置缓存过期时间public Boolean expire(String key, long timeout) {return redisTemplate.expire(key, timeout, TimeUnit.SECONDS);}// 删除缓存public Boolean delete(String key) {return redisTemplate.delete(key);}// 批量获取缓存Key(用于主动更新)public Set<String> keys(String pattern) {return redisTemplate.keys(pattern);}
}

三、更新触发层实现:主动更新避免数据滞后

3.1 定时任务预计算:缓存过期前刷新

通过Spring定时任务,在缓存过期前30分钟主动更新排行榜数据,避免缓存过期时大量请求查库导致“缓存击穿”:

package cn.juwatech.rank.task;import cn.juwatech.cache.RedisService;
import cn.juwatech.rank.service.GoodsRankService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.Set;@Component
public class GoodsRankPreloadTask {@Autowiredprivate RedisService redisService;@Autowiredprivate GoodsRankService goodsRankService;// 热门商品榜缓存前缀private static final String HOT_RANK_KEY_PREFIX = "taoke:rank:goods:hot:";// 定时任务执行频率:每30分钟一次(缓存过期前30分钟刷新)@Scheduled(cron = "0 0/30 * * * ?")public void preloadHotGoodsRank() {// 1. 获取所有热门商品榜缓存KeySet<String> rankKeys = redisService.keys(HOT_RANK_KEY_PREFIX + "*");if (rankKeys == null || rankKeys.isEmpty()) {return;}// 2. 遍历Key,主动更新缓存rankKeys.forEach(key -> {// 提取品类ID(从Key中截取:taoke:rank:goods:hot:3C → 3C)String categoryId = key.substring(HOT_RANK_KEY_PREFIX.length());// 主动查询并更新缓存(topN=100,覆盖热门榜前100名)goodsRankService.getHotGoodsRank(categoryId, 100);});}
}

3.2 数据变更事件通知:实时触发缓存更新

当核心数据(如商品返利比例、销量)变更时,通过Spring事件机制主动删除对应缓存,触发重新加载,确保数据一致性:

package cn.juwatech.goods.event;// 1. 定义商品数据变更事件
public class GoodsDataChangeEvent {private String goodsId;private String categoryId;// 变更类型:REBATE(返利比例变更)、SALES(销量变更)private String changeType;// 构造器、getter省略public GoodsDataChangeEvent(String goodsId, String categoryId, String changeType) {this.goodsId = goodsId;this.categoryId = categoryId;this.changeType = changeType;}
}// 2. 事件发布者(商品服务中调用)
package cn.juwatech.goods.service;import cn.juwatech.goods.event.GoodsDataChangeEvent;
import cn.juwatech.goods.mapper.GoodsMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;@Service
public class GoodsService {@Autowiredprivate GoodsMapper goodsMapper;@Autowiredprivate ApplicationContext applicationContext;// 更新商品返利比例public void updateGoodsRebateRate(String goodsId, String categoryId, double newRate) {// 1. 更新数据库goodsMapper.updateRebateRate(goodsId, newRate);// 2. 发布数据变更事件applicationContext.publishEvent(new GoodsDataChangeEvent(goodsId, categoryId, "REBATE"));}// 更新商品销量public void updateGoodsSales(String goodsId, String categoryId, int sales) {goodsMapper.updateSales(goodsId, sales);applicationContext.publishEvent(new GoodsDataChangeEvent(goodsId, categoryId, "SALES"));}
}// 3. 事件监听器(接收事件并删除缓存)
package cn.juwatech.rank.listener;import cn.juwatech.cache.RedisService;
import cn.juwatech.goods.event.GoodsDataChangeEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;@Component
public class GoodsDataChangeListener {@Autowiredprivate RedisService redisService;private static final String HOT_RANK_KEY_PREFIX = "taoke:rank:goods:hot:";@EventListenerpublic void onGoodsDataChange(GoodsDataChangeEvent event) {// 1. 构建对应品类的排行榜缓存KeyString cacheKey = HOT_RANK_KEY_PREFIX + event.getCategoryId();// 2. 删除缓存(下次请求时会重新查库更新)Boolean deleteResult = redisService.delete(cacheKey);if (deleteResult) {// 日志记录(简化实现)System.out.println("Delete goods rank cache: " + cacheKey + ", changeType: " + event.getChangeType());}}
}

四、策略优化:缓存预热与降级处理

4.1 系统启动缓存预热

为避免系统重启后首次请求查库,在服务启动时预热核心排行榜缓存:

package cn.juwatech.rank.init;import cn.juwatech.rank.service.GoodsRankService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;// 系统启动后执行
@Component
public class RankCachePreloader implements CommandLineRunner {@Autowiredprivate GoodsRankService goodsRankService;// 核心品类ID列表(3C、美妆、母婴)private static final String[] CORE_CATEGORY_IDS = {"3C", "BEAUTY", "MATERNAL"};@Overridepublic void run(String... args) throws Exception {// 预热核心品类的热门商品榜(topN=100)for (String categoryId : CORE_CATEGORY_IDS) {goodsRankService.getHotGoodsRank(categoryId, 100);}}
}

4.2 缓存降级:数据库压力过大时的兜底方案

当数据库CPU使用率超80%时,暂时使用过期缓存,避免数据库雪崩:

package cn.juwatech.rank.service;import cn.juwatech.monitor.DBMonitorService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class GoodsRankService {// 新增数据库监控服务依赖@Autowiredprivate DBMonitorService dbMonitorService;public List<GoodsRankDTO> getHotGoodsRank(String categoryId, int topN) {String cacheKey = HOT_RANK_KEY_PREFIX + categoryId;Set<String> cachedRank = redisService.zReverseRange(cacheKey, 0, topN - 1);// 缓存未命中,但数据库压力过大(CPU>80%):返回过期缓存(若存在)if ((cachedRank == null || cachedRank.isEmpty()) && dbMonitorService.getDbCpuUsage() > 80) {// 获取已过期但未删除的缓存(Redis默认不会立即删除过期Key)Set<String> expiredRank = redisService.zReverseRange(cacheKey, 0, topN - 1);if (expiredRank != null && !expiredRank.isEmpty()) {return expiredRank.stream().map(this::parseGoodsRankDTO).collect(Collectors.toList());}}// 后续逻辑不变...}
}

通过上述混合缓存策略,返利APP的热门商品榜接口响应时间从800ms降至15ms,缓存命中率稳定在99.7%,数据一致性延迟控制在3分钟内。在双11大促期间,排行榜相关请求未对数据库造成压力,完全满足高并发场景需求。

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

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

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

相关文章

科技信息差(9.12)

AI量子计算重塑药物研发&#xff1a;技术融合路径与产业革命一、引言&#xff1a;技术融合的颠覆性机遇2025年9月&#xff0c;AI药物研发公共服务平台正式上线&#xff0c;宣称可将新药上市时间缩短近半1。与此同时&#xff0c;量子计算与AI的跨界合作在KRAS抑制剂开发中取得突…

Java 分布式缓存实现:结合 RMI 与本地文件缓存

目录 一、核心思路 二、项目结构说明 2.1 服务端项目结构&#xff08;IDEA&#xff09; 2.2 客户端项目结构&#xff08;Eclipse&#xff09; 三、服务端实现&#xff08;IDEA&#xff09; 3.1 数据库访问层 3.2 远程接口定义 3.3 远程服务实现 3.4 服务端启动类 四、…

Electron第一个应用

1、安装node nodeJS下载 2、下载完成&#xff0c;需要配置环境。 写道path路径 、 3、安装完成&#xff0c;查看版本 npm -v4、 配置cnpm npm install -g cnpm --registryhttps://registry.npmmirror.com5、参考Electron 写&#xff1a; Electron第一个程序hello 6、安装…

React 原理篇 - React 新架构深度解析

使用过 React v16 之前版本的开发者或许都经历过这样的场景&#xff1a;当页面包含复杂组件或大量列表时&#xff0c;输入框打字会卡顿&#xff0c;滚动会不流畅。这些体验问题的背后&#xff0c;往往与 React 的渲染机制密切相关。2017 年 React v16 推出的 Fiber 架构&#x…

【JavaSE五天速通|第三篇】常用API与日期类篇

适合有其他语言基础想快速入门JavaSE的。用的资料是 Java入门基础视频教程 &#xff0c;从中摘取了笔者认为与其他语言不同或需要重点学习的内容 常用API与日期类只需要有印象即可&#xff0c;用到了再来这查 day04 常用API 一、StringBuilder类 StringBuilder代表可变字符…

K8s学习笔记(二) Pod入门与实战

1 K8s核心资源Pod 1.1 Pod是什么&#xff1f; 官方文档&#xff1a;Pod | Kubernetes Pod 是 Kubernetes&#xff08;k8s&#xff09;中最小的部署与调度单元&#xff0c;并非直接运行容器&#xff0c;而是对一个或多个 “紧密关联” 容器的封装。 核心特点可简单总结为 3 …

用 Python 调用 Bright Data MCP Server:在 VS Code 中实现实时网页数据抓取

用 Python 调用 Bright Data MCP Server&#xff1a;在 VS Code 中实现实时网页数据抓取&#xff0c;本文介绍了Bright Data的Web MCP Server&#xff0c;这是一款能实现实时、结构化网页数据访问的API&#xff0c;适用于AI应用等场景。其支持静态与动态网页&#xff0c;前3个月…

SPSS绘制ROC曲线并计算灵敏度、特异度

SPSS绘制ROC曲线并计算灵敏度、特异度。 &#xff08;1&#xff09;绘制ROC曲线&#xff1a; 输入&#xff1a;预测值、受试者标签。 在SPSS中点击“分析”-“分类”-“ROC曲线” 变量输入&#xff1a;检验变量输入预测值&#xff0c;状态变量输入受试者标签&#xff0c;如果标…

Modbus协议原理与Go语言实现详解

目录 Modbus协议概述协议架构与通信模式Modbus数据模型Modbus协议帧格式功能码详解Go Modbus库完整实现高级应用示例调试与故障排除 Modbus协议概述 Modbus是一种串行通信协议&#xff0c;由Modicon公司&#xff08;现施耐德电气&#xff09;于1979年开发&#xff0c;用于PL…

下载CentOS 7——从阿里云上下载不同版本的 CentOS 7

没有废话&#xff0c;直接上干货。跟着图片教程&#xff0c;一步一步来就行。 想下载其它版本的&#xff0c;自己可以再选择其它的就行。 想省事的朋友可以直接点击: 1、下载页面链接 2、CentOS-7-x86_64-DVD-2207-02(4.4GB).iso

SpringBoot -原理篇

文章目录配置优先级Bean管理获取beanbean作用域第三方beanSpringBoot原理起步依赖自动配置自动配置原理方案源码跟踪原理分析 Conditional案例&#xff08;自定义starter&#xff09;案例&#xff08;自定义starter分析&#xff09;案例&#xff08;自定义starter实现&#xff…

JavaScript与jQuery:从入门到面试的完整指南

JavaScript与jQuery&#xff1a;从入门到面试的完整指南 第一部分&#xff1a;JavaScript基础 1.1 JavaScript简介 JavaScript是一种轻量级的解释型编程语言&#xff0c;主要用于Web开发&#xff0c;可以为网页添加交互功能。它是ECMAScript规范的一种实现。 // 第一个JavaScri…

解决:Ubuntu、Kylin、Rocky系统中root用户忘记密码

解决Linux系统中root用户忘记密码 Ubuntu2204 重启电脑&#xff0c;启动时&#xff0c;长按Shift键&#xff08;对于 BIOS 系统&#xff09;或 Esc 键&#xff08;对于 UEFI 系统&#xff09;进入GRUB菜单 步骤1&#xff1a;重启Ubuntu系统&#xff0c;长按Shift键进入Ubuntu…

ENVI系列教程(二)——自定义坐标系(北京 54、西安 80、2000 坐标系)

目录 1 概述 1.1 地理投影的基本原理 1.2 国内坐标系介绍 1.3 参数的获取 2 详细操作步骤 2.1 添加椭球体 2.2 添加基准面 2.3 定义坐标系 2.4 使用自定义坐标系 1 概述 1.1 地理投影的基本原理 常用到的地图坐标系有 2 种,即地理坐标系和投影坐标系。地理坐标系是…

一种基于因果干预的少样本学习的故障诊断模型

一、研究背景与问题 ​工业背景​:机械故障诊断对工业系统安全至关重要,但实际中故障样本稀少,难以训练传统深度学习模型。 ​现有问题​: 当前少样本学习(FSL)方法大多基于相关性而非因果关系建模,容易学习到伪相关特征,导致模型可解释性差、泛化能力弱。 跨组件故障诊…

机器视觉光源的尺寸该如何选型的方法

机器视觉光源的尺寸该如何选型的方法&#x1f3af;机器视觉光源的尺寸选型的方法&#x1f3af;一、选型案例&#x1f3af;二、照射方式&#x1f3af;三、镜头选择&#x1f3af;四、光源架构光源的工作距离与视野大小&#x1f3af;五、总结&#xff1a;光源选型 —— 机器视觉检…

HTML新属性

HTML5引入了许多新属性&#xff0c;旨在增强语义化、交互性和多媒体支持。以下是一些重要的新属性及其用途分类&#xff1a;语义化与结构属性data-*&#xff1a;自定义数据属性&#xff0c;允许开发者存储额外信息&#xff08;如data-id"123"&#xff09;。hidden&am…

从工地到链上:一个土建人的 Web3 转行经历

Web3 的风&#xff0c;终究还是吹到了土建行业。2017 年&#xff0c;土建专业&#xff08;给排水工程&#xff09;的刘正源偶然看到一则关于比特币的新闻&#xff0c;被它背后的经济模型与技术架构深深震撼。到了 2021 年&#xff0c;他在工地上再次听人提起区块链&#xff0c;…

20250914-03: Langchain概念:提示模板+少样本提示

20250914-03: Langchain概念&#xff1a;提示模板少样本提示 聊天模型 消息 提示 结构化输出 &#x1f3af; 学习目标 掌握如何“喂给模型正确的输入”并“解析出想要的输出”。 &#x1f517; 核心概念 ​聊天模型&#xff08;ChatModel&#xff09;​消息&#xff08;M…

【AI推理部署】Docker篇04—Docker自动构建镜像

Docker 自动构建镜像1. Dockfile 编写2. 镜像使用使用 Dockerfile 构建镜像 Dockerfile 其实就是把我们前面的一系列安装、配置命令写到一个文件中&#xff0c;通过 docker build 命令&#xff0c;一键完成镜像的构建。接下来&#xff0c;我们以 bitnami/pytorch:2.1.1 作为基础…