一、方案背景与痛点

电商商品排行榜(7 日销量榜、当日日榜、热度榜)是电商核心流量入口,绝大多数开源 / 自研方案存在严重两极化问题:

1. 传统全量 MySQL 计算方案(性能

  • 每天 / 每小时全量扫描几十万 SPU + 大表 JOIN
  • 数据库 CPU 抖动、慢 SQL 堆积、从库压力爆表
  • 99% 冷门商品 0 销量,完全没必要参与计算,但依然被扫描

2. 纯 Redis 全量 ZSet 方案(稳定性

  • 所有商品塞入 ZSet,成员几十万,内存爆炸
  • ZUNION、遍历、分数衰减极易阻塞 Redis 单线程
  • 冷热商品混在一起,头部更新慢、尾部无效占用资源

3. 复杂分片方案(太重、小团队无法落地)

  • Kafka 集群、小时分片 ZSet、多消费组、分库分表
  • 架构重、维护成本高、中小团队 hold 不住

基于以上痛点,本文落地一套「轻量化、高性能、高稳定、极易落地」的工业级排行方案
核心原创思路:只有今日销量达到阈值的商品,才允许进入 TOP 榜单 ZSet,冷门全程不计算、不进缓存、不耗性能;同时拆分当日销量日榜7 日综合热度榜两套独立榜单,解决零点切换空白、新开盘数据稀少问题。

二、本方案核心设计理念

1. 核心思路

  • 冷门商品:只 Redis 计数,不参与排行、不进 ZSet、不查库
  • 潜力黑马:今日销量达标阈值,才触发完整分数计算 + 冲榜比对
  • 头部爆款:常驻 ZSet,实时增量更新排名,豁免销量门槛
  • 数据真值:以 MySQL 汇总表为准,Redis 只做实时展示与准入判断
  • 双榜单隔离:7 日综合榜长期稳定;当日日榜双 key 轮换兜底,杜绝页面空白

2. 方案优势

✅ 彻底无全表扫描:只计算有动销、达门槛的商品
✅ Redis 内存极小:ZSet 只留存有效头部商品
✅ 黑马实时上榜:新品爆单无需等待凌晨刷新
✅ 数据库压力极低:95% 商品不走聚合计算
✅ 架构极简:无需小时分片 ZSet、无需复杂合并
✅ 容错极强:Redis 丢数据可秒级重建
✅ 页面零空白:当日榜单量不足自动降级复用昨日榜单

三、整体架构分层(最简四层)

1. 采集层(异步解耦,不卡下单主链路)

订单支付、退款、取消订单 → MQ (RocketMQ/Kafka) 异步投递
主业务链路零 Redis、零 DB 排行计算逻辑,保证下单 RT 最低。

2. 实时计数层(Redis 今日销量计数)

采用 String 结构单商品独立计数:

  • 今日单品销量 Key:rank:sale:today:{spuId}
  • 今日动销白名单:rank:sale:today:whitelist(Set 结构,记录今天有成交的所有商品,避免全量 Scan)
    所有成交商品仅做两件事:计数累加 + 加入白名单。不达阈值完全不参与后续任何排行计算。

3. 阈值准入层(核心精髓)

后台可动态配置阶梯阈值:

  • 常态白天模式:普通商品冲榜阈值 = 20 单,≥20 单才允许写入榜单 ZSet
  • 凌晨低谷模式(0:00-8:00):阈值自动下调至 5 单,适配低流量时段
  • 头部爆款白名单、15 天扶持新品:无销量门槛,成交 1 单即可更新榜单

4. 基准校准层(兜底保精准)

  • 小时级:DB 修正 Redis 计数偏差,防止消息丢包、退款导致分数漂移
  • 零点级:归档单日销量,轮换当日榜单新旧 key
  • 凌晨级:全量动销商品重算 7 日总分,整体覆盖刷新 7 日综合榜单

四、数据结构设计(两张 MySQL 表 + 全套 Redis 键)

1. MySQL 核心汇总表(唯一真值来源)

(1)小时销量预汇总表(规避原始订单大表 JOIN)

sql
CREATE TABLE order_hour_spu (
    spu_id BIGINT NOT NULL,
    stat_hour DATETIME NOT NULL,
    sale_num BIGINT DEFAULT 0,
    sale_amount DECIMAL(18,2) DEFAULT 0,
    PRIMARY KEY (spu_id,stat_hour)
) ENGINE=InnoDB;

特点:无动销无数据,天然过滤 90% 滞销商品。

(2)排行基准分数表

sql
CREATE TABLE goods_rank_total (
    spu_id BIGINT NOT NULL,
    rank_type TINYINT NOT NULL COMMENT '1=7日热度 2=当日销量',
    period TINYINT NOT NULL,
    score DECIMAL(18,4) NOT NULL DEFAULT 0,
    seven_sale_num BIGINT DEFAULT 0,
    update_time DATETIME DEFAULT NOW() ON UPDATE NOW(),
    UNIQUE KEY uk_type_period_spu(rank_type,period,spu_id)
);

2. Redis 全套键划分

① 7 日综合热度榜(首页核心展示,零点永久不清空)

Plain Text
rank:hot:7day:top          # 常驻7日TOP2000榜单ZSet
rank:hot:7day:snap:yyyyMMdd # 每日榜单快照,异常回滚用
rank:hot:white_list        # S/A头部爆款白名单Set,豁免20单门槛

② 当日销量日榜(独立 Tab 页,双 key 轮换防空白)

Plain Text
rank:sale:today:curr    # 当日实时榜单ZSet
rank:sale:today:last    # 昨日备份榜单ZSet,数据不足时兜底展示

③ 实时计数层

Plain Text
rank:sale:today:{spuId} # 单品今日实时销量String
rank:sale:today:whitelist # 今日成交商品集合

五、完整执行链路

步骤 1:订单异步计数(日间实时)

支付成功 MQ 消费流程:

  1. 幂等判断 orderId 是否已处理,防止重复加减
  2. Redis INCRBY rank:sale:today:{spuId} 1
  3. SADD rank:sale:today:whitelist {spuId}
  4. 同步 UPSERT 写入 order_hour_spu 数据库落真值

退款 / 取消订单:执行DECRBY反向扣减销量,数值归零后同步从当日 ZSet 移除该商品。

步骤 2:阈值判断与双榜单写入逻辑

读取商品当日实时销量,分三类处理:

  1. 白名单爆款 / 15 天新品:无门槛,任意销量立刻计算分数,同步更新 7 日总榜 + 当日 curr 榜单
  2. 普通商品 销量≥阈值(20/5 动态切换):查询 MySQL 完整分数,执行冲榜对比逻辑
  3. 普通商品 销量<阈值:仅计数,不操作任何 ZSet 榜单

步骤 3:冲榜 Lua 原子逻辑(无并发错乱)

  1. 商品已在 ZSet:直接 ZADD 覆盖最新分数,实时更新排名
  2. 商品不在 ZSet:读取榜单末尾最低分对比
  • 新分数更高:淘汰末位商品,插入当前商品
  • 分数不足:仅保存 MySQL 分数,不占用 Redis 内存

步骤 4:零点平滑切换(核心解决当日榜单空白)

零点不删除 7 日总榜,只处理当日计数与当日榜单轮换:

  1. 读取rank:sale:today:whitelist全部 SPU,批量归档销量写入 order_day_spu 日汇总表;
  2. 当日榜单重命名轮换:RENAME rank:sale:today:curr rank:sale:today:last,昨日榜单完整留存做兜底;
  3. 初始化空集合rank:sale:today:curr作为新一天当日榜单容器;
  4. 批量删除所有rank:sale:today:{spuId}单品计数 key、清空白名单;
  5. 复制rank:hot:7day:top生成当日 7 日榜单快照;
  6. 自动切换凌晨低谷阈值(5 单),放宽当日榜单准入条件。

重点:7 日综合榜单全程保留不动,页面首页永远有完整排行,不受零点重置影响。

步骤 5:当日榜单数据不足兜底逻辑(接口层强制兼容)

页面请求「今日热销」Tab 时,后端统一判断:

  1. 先查询当日实时榜单商品总数 ZCARD rank:sale:today:curr
  2. 配置最低安全阈值(示例:少于 10 条视为数据不足)
  3. 数据充足:正常返回rank:sale:today:curr实时今日排名
  4. 数据不足(凌晨新开盘、全天低成交):自动降级返回rank:sale:today:last昨日完整榜单填充页面,用户无空白感知

伪代码示例:

java
// 查询今日榜单数量
Long todaySize = redis.zcard("rank:sale:today:curr");
String targetKey = "rank:sale:today:curr";
// 不足10条,切换昨日榜单兜底
if (todaySize < 10) {
    targetKey = "rank:sale:today:last";
}
// 返回前N条排行数据
return redis.zrevrangeWithScores(targetKey, 0, 49);

步骤 6:凌晨 2 点全局校准(终极数据兜底)

  1. 仅遍历近 7 天 order_day_spu 中有成交记录的动销商品,绝不扫描全量 SPU;
  2. 批量计算 7 日完整加权热度分数,批量更新 goods_rank_total 基准表;
  3. 内存排序截取 TOP2000,全覆盖刷新rank:hot:7day:top7 日总榜;
  4. 同步刷新rank:hot:white_list头部爆款白名单,更新免门槛商品范围;
  5. 不改动当日 curr 榜单,当日榜单依靠日间订单实时填充。

步骤 7:每 2 小时轻巡检,防止榜单长期僵死

巡检范围仅两个小集合:头部白名单 + 15 天新品池,总量上限 3000 条:

  1. 批量从 MySQL 拉取精准 7 日分数、当日销量分数;
  2. 批量覆盖更新两个榜单 ZSet;
  3. 即便全天极少商品达标销量阈值,榜单每两小时自动校准一次,不会一整天静止不动。

六、冷启动完整解决方案

1. 单个新品 SPU 冷启动

  1. 独立新品池表,扶持周期 15 天,配置阶梯衰减基础分(1-5 天高值、11-15 天低值);
  2. 新品完全不受 20 单阈值限制,成交 1 单即可计算带基础分的总分、冲榜;
  3. 15 天后基础分清零,转为普通商品走阈值规则。

2. Redis 整体宕机 / 清空冷启动

  1. 7 日总榜:直接读取 goods_rank_total 预计算分数,LIMIT 2000 批量 ZADD 重建,毫秒级恢复;
  2. 当日榜单:读取 order_day_spu 今日动销数据重建 curr,无重建间隙时先用 last 昨日榜单兜底页面;
  3. MySQL 汇总表永久留存真值,Redis 只是缓存层,不存在不可逆数据丢失。

3. 平台全新从零搭建冷启动

  1. 回放近 7 天订单消息,批量生成 hour/day 两层汇总表;
  2. 一次性计算全部动销商品分数,初始化 7 日总榜、白名单;
  3. 初始化 curr/last 双当日榜单,切换至正常日间阈值流程。

七、性能、容错、大促降级机制

1. 数据库压力压制

95% 冷门商品无任何聚合查询;计算池永远只有动销 SPU,每日计算量削减 90%+;统计查询全部路由 MySQL 从库。

2. Redis 数据漂移防护

小时级 DB 校对实时销量计数、凌晨全局全覆盖重算,双层抵消加减偏差、消息丢包误差。

3. 退款、刷单数据防失真

支付正向 INCR、退款反向 DECR;全局订单幂等 Set 拦截重复消息;风控过滤虚假订单,脏数据不进入计数链路。

4. 大促峰值降级开关

  • 抬高当日榜单阈值(20→50)
  • 临时关闭浏览、收藏行为权重,仅以销量计分
  • 缩减 TOP 缓存容量(2000→1000)
  • 可临时关闭 2 小时巡检,只保留凌晨一次校准

八、分页查询逻辑(用户无感知分层路由)

  1. 7 日综合榜
  • 排名前 2000:Redis ZREVRANGE 毫秒返回
  • 2000 名之后冷门:自动切换 MySQL goods_rank_total 分页查询

     2.当日销量榜

  • 优先 curr 实时榜单;数量不足自动切 last 昨日榜单兜底
  • 分页深度过大同样兜底走 MySQL 基准表

九、方案横向对比表

方案

数据库压力

Redis 压力

实时性

精准度

落地难度

页面空白风险

全量 MySQL 定时计算

极高

简单,性能差

全量商品塞入 ZSet

极高 (阻塞风险)

简单,不稳定

小时分片重型架构

极低

极高

极高

极重 (集群基建)

本文阈值准入双榜单方案

极低

极低

极高

极简中小团队友好

零空白,自动兜底

十、最终总结

  1. 核心思路「销量阈值准入 ZSet」完美解决冷热资源分配问题,摒弃无效全量计算;
  2. 7 日综合榜单常驻不删除,天然保障首页流量兜底;
  3. 当日销量榜单采用 curr/last 双 key 轮换,接口层自动识别数据多少,不足时复用昨日榜单,彻底解决零点新开页面空白;
  4. 白名单爆款、新品豁免门槛、凌晨动态降阈值、两小时巡检多层兜底,不会出现榜单一整天静止不动;
  5. 整套体系以 MySQL 汇总数据为唯一真值,Redis 只做加速缓存,容错、扩容、大促降级全部配套完善,十万、百万 SPU 体量均可稳定长期运行,是垂直电商、自营商城、创业团队性价比最高的排行榜落地方案。

Logo

电商企业物流数字化转型必备!快递鸟 API 接口,72 小时快速完成物流系统集成。全流程实战1V1指导,营造开放的API技术生态圈。

更多推荐