news 2026/8/20 11:06:34

统计聚合表设计:唯一键防重、每日 Job 落库与趋势补零

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
统计聚合表设计:唯一键防重、每日 Job 落库与趋势补零

模块:yudao-module-statistics
关键类/脚本:TradeStatisticsServiceImplTradeOrderStatisticsServiceImpl#fillTrendGaps22-pro-statistics-performance.sql
关键词:统计聚合表设计、唯一键防重、趋势图补零、商城数据库设计

摘要

趋势图"缺日"、重复统计行、补跑失控,是电商统计系统的三大经典事故。本文介绍久滴直播电商系统在 P0 统计修复中的落库层设计:(tenant_id, time)唯一键从 DB 层杜绝重复、每日 Job 幂等落库、fillTrendGaps游标补零保证 X 轴连续,以及补跑接口的天数上限防护。

技术背景

久滴的统计架构是"T+1 落库 + 今日实时计数"双层模型:

  • 昨日及以前:由tradeStatisticsJob每日 00:30 聚合trade_order等表写入trade_statistics,看板读落库表,毫秒级返回;
  • 今日:由 Redis 实时计数器承担(见系列文章 01)。

这一模型曾暴露三个 P0 问题:

  1. 重复行product_statistics实测存在 16 组(tenant_id, time)重复数据——应用层 check-then-act 防重在多实例部署/Job 重跑下失效;
  2. 趋势缺日GROUP BY DATE(pay_time)对无订单日期直接不产出行,前端折线图断裂;
  3. 对照错位:今日 vs 昨日的对照曲线按date字段匹配,两个区间日期本就不重叠,匹配永远落空。

实现方案

1. 唯一键:把防重从应用层下沉到 DB 层

22-pro-statistics-performance.sql核心变更:

-- trade_statistics 无历史重复,直接添加ALTERTABLEtrade_statisticsADDUNIQUEKEYuk_tenant_time(tenant_id,time);-- product_statistics 先去重(保留 id 最小的一条),再加唯一键DELETEpFROMproduct_statistics pINNERJOIN(SELECTMIN(id)ASkeep_id,tenant_id,timeFROMproduct_statisticsGROUPBYtenant_id,timeHAVINGCOUNT(*)>1)dupONp.tenant_id=dup.tenant_idANDp.time=dup.timeANDp.id<>dup.keep_id;ALTERTABLEproduct_statisticsADDUNIQUEKEYuk_tenant_time(tenant_id,time);

设计权衡:

  • 为什么唯一键含 tenant_id 前缀:多租户拦截器为每条 SQL 追加tenant_id条件,前缀索引/唯一键能精确定位单租户区段;
  • 为什么允许冲突失败而不做 upsert:同一天同一口径的重复统计数值必然一致,唯一键冲突即说明 Job 重跑,直接失败是最安全的幂等语义;
  • 先去重再加键的顺序不可颠倒,否则 ALTER 会因存量重复报Duplicate entry

2. 补跑接口与天数上限

历史数据初始化或 Job 中断后需补跑,statisticsTrade(days)支持按天数回溯:

/** 补跑天数上限:防止误传超大参数长时间占用线程 */privatestaticfinalintMAX_STATISTICS_DAYS=1095;@OverridepublicStringstatisticsTrade(Integerdays){if(days>MAX_STATISTICS_DAYS){thrownewIllegalArgumentException("交易统计补跑天数不能超过 "+MAX_STATISTICS_DAYS+" 天");}// 逐日回溯:IntStream.rangeClosed(1, days) 对每一天调用单日统计并汇总结果...}

上限 1095 天(3 年)的来源:覆盖业务需要的最长回溯窗口,同时避免误传days=99999让单个请求占用线程数小时拖垮服务。实际补跑经验:930 天历史数据通过管理后台"手动执行 Job + 传参"一次完成。

3. fillTrendGaps:游标补零保证 X 轴连续

趋势接口返回前统一过补零函数,按天/月游标生成完整日期序列:

privateList<TradeOrderTrendRespVO>fillTrendGaps(List<TradeOrderTrendRespVO>list,LocalDateTimebeginTime,LocalDateTimeendTime,booleanbyMonth){// 1. 查询结果转 Map(date -> VO),重复日期取第一条兜底Map<String,TradeOrderTrendRespVO>map=list.stream().collect(Collectors.toMap(TradeOrderTrendRespVO::getDate,Function.identity(),(a,b)->a));// 2. 按天/月游标从 beginTime 走到 endTime,生成完整日期序列// 月粒度 yyyy-MM(对齐 GROUP BY DATE_FORMAT('%Y-%m')),日粒度 yyyy-MM-ddList<String>keys=/* 游标逐日/逐月推进生成的全部日期 */;// 3. 逐 key 查 Map,缺失的补零行(orderPayCount=0, orderPayPrice=0)returnkeys.stream().map(key->map.getOrDefault(key,newTradeOrderTrendRespVO().setDate(key).setOrderPayCount(0).setOrderPayPrice(0))).collect(Collectors.toList());}

要点:

  • 补零在服务端而非前端:前端无法判断"缺失"与"为 0",服务端补零后接口契约变为"返回区间内连续完整序列";
  • (a, b) -> a合并策略:即使底层查询意外返回重复日期,取第一条兜底,不抛异常;
  • 月粒度用yyyy-MM格式:与GROUP BY DATE_FORMAT(pay_time, '%Y-%m')的输出格式严格对齐,type=365(YEAR)时自动切换。

4. 对照数据的序号对齐(修复隐蔽 Bug)

对照场景(今日 vs 昨日、本周 vs 上周)两个区间的日期必然不同,原实现按date查找对照点永远返回 null。修复后先对两侧分别fillTrendGaps,再按序号对齐:当前区间第 N 天对应对照区间第 N 天。补零在此承担了第二职责——它让两个不等长序列长度一致,序号对齐才有意义。

注意事项

  1. 加索引/唯一键选业务低峰期执行trade_order是大表,DDL 期间会锁表(MySQL 8.0 的 instant/online DDL 对加索引较友好,但仍建议避开高峰);
  2. 验证 SQL
    SHOWINDEXFROMtrade_statistics;-- 应见 uk_tenant_timeSELECTtenant_id,time,COUNT(*)cFROMproduct_statisticsGROUPBYtenant_id,timeHAVINGc>1;-- 应为空集
  3. 补跑前先确认口径:补跑按当前代码口径重算,若口径曾变更,历史区间会被"新口径"覆盖——先在对账 Job 通过后小范围试跑;
  4. 不要手工 INSERT 统计表:绕过 Job 插入的行若与后续 Job 冲突会唯一键报错,一律通过"手动执行 Job 传参"补数。

关于久滴直播电商系统

JiuDi Live-Mall Service— 面向直播电商场景的全栈后端 Pro 版解决方案。久滴直播电商系统 —— 基于芋道框架深度定制的 SaaS 级直播电商解决方案,支持多租户、实时统计与腾讯云直播集成。

🎥久滴成河,直播成商·Drop by drop, live commerce flows.

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/20 11:06:01

Rust HTTP客户端数据完整性校验:CRC-32在JSON反序列化前的应用实践

1. 为什么要在反序列化前校验数据完整性在 Rust 里处理 HTTP 请求&#xff0c;拿到 JSON 数据直接扔给serde_json反序列化&#xff0c;是很多新手甚至老手会写的代码。这看起来没问题&#xff0c;直到你遇到一次线上故障&#xff1a;客户端传过来的数据在网络传输中因为某些原因…

作者头像 李华
网站建设 2026/8/20 11:03:17

【计算机毕业设计单片机案例】基于 STM32 ESP01 的物联网智能柜体远程监控平台设计 基于 STM32 的智能柜体换气除湿消毒一体化系统实现(012004)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机&#xff0c;Java、小程序技术领域和毕业项目实战 ✌️…

作者头像 李华
网站建设 2026/8/20 10:59:22

Android Studio 全界面汉化:三步装好官方同款中文语言包

Android Studio 全界面汉化&#xff1a;三步装好官方同款中文语言包 【免费下载链接】AndroidStudioChineseLanguagePack AndroidStudio中文插件(官方修改版本&#xff09; 项目地址: https://gitcode.com/gh_mirrors/an/AndroidStudioChineseLanguagePack AndroidStudi…

作者头像 李华
网站建设 2026/8/20 10:57:40

SPSS描述统计量:平均数、中位数、标准差的选择与应用

1. 先搞清楚这些统计量到底在描述数据的什么特征当你拿到一份数据&#xff0c;比如一群人的身高、一个月的销售额、或者一批产品的测试分数&#xff0c;第一件事就是看看这些数字大概长什么样。SPSS里的这些描述统计量&#xff0c;就是帮你快速“看”懂数据的工具。它们不是一堆…

作者头像 李华