网易首页 > 网易号 > 正文 申请入驻

美团内部方案:5 亿优惠券每天 100 万过期,从不扫主表!(附SQL)

0
分享至

场景

某平台有1亿用户,平均每个用户5张优惠价,一共5亿条优惠价数据,都在一张表里(表结构如下),每天有 100 万张优惠券过期,怎么设计处理系统?

CREATE TABLE coupons (id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',coupon_code VARCHAR(64) NOT NULL COMMENT '优惠券唯一码(用户可见)',user_id BIGINT NOT NULL COMMENT '用户ID',status TINYINT NOT NULL DEFAULT 1 COMMENT '状态:1-未使用,2-已使用,3-已过期,4-已冻结',denomination DECIMAL(10,2) NOT NULL COMMENT '面额(如10元)',expire_time DATETIME NOT NULL COMMENT '过期时间',create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '领取时间',used_time DATETIME NULL COMMENT '使用时间',PRIMARY KEY (id),UNIQUE KEY uk_coupon_code (coupon_code) COMMENT '唯一码防重复',KEY idx_user_status (user_id, status) COMMENT '用户查可用券:按用户+未使用筛选')ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='优惠券主表';
一、定时扫主表方案(危险)

使用调度中心定时扫描优惠卷表修改优惠价状态,执行如下SQL:

update couponsSET status = '3'WHERE expire_time < NOW() AND status = 'unused';

这个方案有如下问题:

A、数据库性能灾难

主表的expire_time和status如果没有联合索引,会触发全表扫描 ,会占很多的数据库 I/O 和 CPU,导致核心业务超时。

即使加了索引避免了全表扫描,若过期券数量很大,单次更新会生成巨大的事务日志(redo/undo log),日志刷盘会占用磁盘 IO,同样影响数据库稳定性。

B、锁竞争:阻塞正常业务操作

MySQL 的 InnoDB 引擎在执行 UPDATE 时,会对匹配的行加 “行锁”。如果单次更新 100 万条记录,这些行锁会持有几分钟甚至十几分钟,期间用户若要使用其中某张券(比如用户刚好想用一张快过期的券付款),会被阻塞直到锁释放,直接导致业务卡顿。

C、数据一致性风险

如果定时任务执行到一半(比如更新了 50 万条后),数据库突然宕机,重启后无法判断 “哪些券已经更新,哪些没更新”—— 重新执行会导致重复更新(把已过期的券再更一遍,虽不影响状态,但冗余操作),不执行又会遗漏 50 万条,最终导致 “部分过期券仍显示为可用“,引发业务问题。

二、过期任务表方案(解耦、推荐)

某大厂的核心思路是 “用轻量任务表减轻主表扫描压力”,细节如下:

1、剥离出 优惠券过期任务表(coupon_expire_task):仅服务过期处理

CREATE TABLE coupon_expire_task (id BIGINT NOT NULL AUTO_INCREMENT COMMENT '任务ID',coupon_code VARCHAR(64) NOT NULL COMMENT '优惠券编号',expire_time DATETIME NOT NULL COMMENT '优惠券过期时间',task_status TINYINT NOT NULL DEFAULT 0 COMMENT '任务状态, 0-待处理,1-已处理,2-处理失败',create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '任务创建时间',PRIMARY KEY (id),UNIQUE KEY uk_coupon_code (coupon_code) COMMENT '避免重复创建任务',KEY idx_expire_taskstatus (expire_time,task_status) COMMENT '核心索引,查待处理的过期任务') ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='优惠券过期任务表';

这张表是 “轻量中间层”,只存 3 个关键信息:coupon_code(关联主表)、expire_time(判断过期)、task_status(任务状态),数据量虽大但单条记录小,查询效率高。

关键设计:idx_expire_taskstatus联合索引, 让“查询过期且未处理的任务”(WHERE expire_time < NOW() AND task_status = 0)能走索引,避免全表扫描,这是整个方案的性能核心。

2、核心业务点

(1)用户领券:双表事务同步,确保任务不遗漏

用户领取优惠券时,必须同时在 “主表” 和 “任务表” 插入数据,且用事务保证一致性。

(2)用户用券:清理任务表,避免无效扫描

用户使用优惠券时,主表状态会更新为 “已使用”,此时任务表的“过期任务” 已无意义 —— 必须同步删除或标记任务表记录,避免定时任务重复扫描已用券。

(3)过期处理:定时任务批量处理,控制负载

用分布式定时任务调度器按固定频率(如 1 分钟)触发,每次只处理一批数据(如 1000 条),避免单次任务负载过高。

核心逻辑如下:

// 分布式定时任务:每分钟执行一次@XxlJob("handleExpiredCoupon")public void handleExpiredCoupon() {// 1、批量查询待处理的过期任务(每次1000条,控制负载)List taskList = expireTaskMapper.selectExpiredTasks(new Date(), 0, 1000);// 2、批量修改优惠卷主表的状态为“已过期”List couponCodes = taskList.stream().map(CouponExpireTask::getCouponCode).collect(Collectors.toList());couponMapper.batchUpdateStatus(couponCodes, 3, new Date());// 3、批量修改任务表状态为“已处理”List taskIds = taskList.stream().map(CouponExpireTask::getId).collect(Collectors.toList());expireTaskMapper.batchUpdateTaskStatus(taskIds, 1)}

(4)定期清理任务表,保持轻量化

任务表的 “已处理” 记录会越来越多,需要另一个定时任务(如每天凌晨 3 点),删除 “已处理且创建时间超过 30 天” 的记录。

DELETE FROM coupon_expire_taskWHERE task_status = 1   AND create_time < DATE_SUB(NOW(),INTERVAL 30 DAY);

3、方案优势

性能可控:定时任务只扫描轻量的任务表,且走联合索引,避免主表全表扫描;每次处理 1000 条,不会压垮数据库;

可靠性高:领券用事务保证双表一致,过期处理 “先更主表、再更任务表”—— 即使中间失败,任务表仍是 “待处理”,下轮仍可处理;

架构解耦:过期处理逻辑独立于 “领券、用券” 核心业务,后续可单独调整定此逻辑,不影响核心业务。

  • MySQL8 磁盘只剩 10G,380G 巨表不敢 DELETE/ALTER?一招原地瘦身 70%
  • MySQL 30 个配置改完 TPS 翻 10 倍,结果内存纹丝不动:60% 稳如老狗
  • 同样的 SQL,为啥别人 1000 并发不卡?99% 程序员没注意的行锁细节
  • MySQL多表关联使用 NLJ 发挥最大效能
  • xxl_job巧妙的设计保证任务精准调度
  • 同样是分库分表,为啥淘宝查订单这么快?核心就这 2 个设计
  • 百万用户 IM 不卡顿?秘密藏在 3 张数据库表,信箱表设计太巧妙了

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
大陆通报2件事,统一倒计时启动?吕秀莲带头改口,痛骂“台独”

大陆通报2件事,统一倒计时启动?吕秀莲带头改口,痛骂“台独”

悦心知足
2026-01-07 09:14:50
田朴珺拿走人脉,王石只剩年龄

田朴珺拿走人脉,王石只剩年龄

深水财经社
2026-01-05 21:19:06
中国“人造太阳”再出利好,可控核聚变上涨,10家核心受益企业

中国“人造太阳”再出利好,可控核聚变上涨,10家核心受益企业

元爸体育
2026-01-05 14:45:42
国际奥委会后悔当年对北京挑剔,愁眉不展引体育盛事高低落差热议

国际奥委会后悔当年对北京挑剔,愁眉不展引体育盛事高低落差热议

诗意世界
2026-01-03 10:00:22
千万不要过度体检?医生再三提醒:55岁后,这5种体检能不做就不做

千万不要过度体检?医生再三提醒:55岁后,这5种体检能不做就不做

神奇故事
2025-12-30 23:09:45
梅西:新一代阿根廷球员会集体等我吃饭;我不擅长沟通和表达

梅西:新一代阿根廷球员会集体等我吃饭;我不擅长沟通和表达

懂球帝
2026-01-07 10:24:19
巴媒:巴西攻击手阿尔贝托即将与一家中国俱乐部达成协议

巴媒:巴西攻击手阿尔贝托即将与一家中国俱乐部达成协议

懂球帝
2026-01-06 19:05:13
几千年来,为什么游牧骑兵不去打欧洲,非要和东亚过不去呢?

几千年来,为什么游牧骑兵不去打欧洲,非要和东亚过不去呢?

历史按察使司
2026-01-06 08:05:03
姑姑未婚未育,娘家默认遗产归侄子,45岁决定结婚,娘家崩溃拦婚

姑姑未婚未育,娘家默认遗产归侄子,45岁决定结婚,娘家崩溃拦婚

半夏解语
2026-01-05 07:00:03
泡泡玛特回应“三星掌门人在北京买了100个Labubu”

泡泡玛特回应“三星掌门人在北京买了100个Labubu”

鲁中晨报
2026-01-06 21:09:13
车在桥上,人在哪里?38岁男子元旦失联:留下一句“人生没意义”

车在桥上,人在哪里?38岁男子元旦失联:留下一句“人生没意义”

奇思妙想草叶君
2026-01-05 20:57:05
吴营村慌了!吴承言宣布全国巡画后连夜修路,终于把收割机挪走了

吴营村慌了!吴承言宣布全国巡画后连夜修路,终于把收割机挪走了

观察鉴娱
2026-01-07 09:12:45
女性的私处哪种形态更好?女性阴部的形状类型有哪些?不妨来了解

女性的私处哪种形态更好?女性阴部的形状类型有哪些?不妨来了解

医者荣耀
2025-12-25 12:05:06
网红郭有才,泡沫散去之后,曾经飘的有多高,如今摔得就有多惨

网红郭有才,泡沫散去之后,曾经飘的有多高,如今摔得就有多惨

小熊侃史
2025-12-20 10:55:18
直播收入不理想,女主播为“求打赏”给粉丝私发千部不雅视频,一审获刑十年,二审改判两年

直播收入不理想,女主播为“求打赏”给粉丝私发千部不雅视频,一审获刑十年,二审改判两年

极目新闻
2026-01-06 20:22:36
蔚来第100万台量产车赠给量子科学家

蔚来第100万台量产车赠给量子科学家

新浪财经
2026-01-07 08:59:00
皇马TV记者:坎塞洛这笔赔钱转会很可疑,有巴萨参与就更可疑

皇马TV记者:坎塞洛这笔赔钱转会很可疑,有巴萨参与就更可疑

懂球帝
2026-01-07 10:03:20
中国将迎来前所未有的死亡高峰,专家得出答案,主要3个因素导致

中国将迎来前所未有的死亡高峰,专家得出答案,主要3个因素导致

小怪吃美食
2026-01-07 04:32:20
连广州都收不上居民医保的钱了

连广州都收不上居民医保的钱了

凯利经济观察
2026-01-06 09:33:50
高洪波1月6日亮相新去向,已被足协再次官宣委以重任,值得期待

高洪波1月6日亮相新去向,已被足协再次官宣委以重任,值得期待

振刚说足球
2026-01-07 09:33:22
2026-01-07 10:47:00
呼呼历史论
呼呼历史论
分享有趣的历史
261文章数 15959关注度
往期回顾 全部

科技要闻

马斯克杀疯了!xAI官宣200亿美元融资

头条要闻

李文荣被公诉:泄露尚未公开企业重组信息 搞权色交易

头条要闻

李文荣被公诉:泄露尚未公开企业重组信息 搞权色交易

体育要闻

全明星次轮票数:东契奇票王 詹皇超KD升西部第8

娱乐要闻

2026年央视春晚彩排:沈腾确定回归

财经要闻

茅台为何要和分销商彻底说拜拜?

汽车要闻

摩登出街潮品 实拍奇瑞QQ冰淇淋女王版

态度原创

游戏
手机
本地
艺术
公开课

小团体闹麻了!《黎明行者》称本作吸血鬼会组团争斗

手机要闻

2亿像素还得等等 苹果计划2028年首次搭载

本地新闻

云游内蒙|初见呼和浩特,古今交融的北疆都会

艺术要闻

你也在学书法?先楷后行的秘密曝光!

公开课

李玫瑾:为什么性格比能力更重要?

无障碍浏览 进入关怀版