首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >报名名额满了,有人取消怎么按顺序补位:候补队列与释放通知设计

报名名额满了,有人取消怎么按顺序补位:候补队列与释放通知设计

原创
作者头像
用户5598620
发布于 2026-09-29 14:00:49
发布于 2026-09-29 14:00:49
770
举报

一场活动名额 50 个,第 51 个报名者怎么办?直接拒绝会损失用户,登记候补又怕补位顺序乱、同一人重复通知、几个人同时取消时补位打架。读完你能拿到一套"候补登记 + 释放事件 + 按序补位 + 确认窗口"的设计,附表结构与并发写法。队列怎么排、名额谁释放、补位怎么确认,三步立住,几百人同时操作也不乱。

一、候补先解决"谁能排上":登记表与唯一约束

候补的本质是"名额释放后的按序补位",第一步是把候补关系登记清楚,一个活动、一个用户只允许一条有效候补:

代码语言:sql
复制
CREATE TABLE waitlist (
  id          BIGINT PRIMARY KEY AUTO_INCREMENT,
  activity_id BIGINT NOT NULL,
  user_id     BIGINT NOT NULL,
  seq_no      INT NOT NULL,               -- 排队序号,同一活动自增
  status      VARCHAR(16) NOT NULL DEFAULT 'WAITING', -- WAITING/OFFERED/CONFIRMED/EXPIRED/CANCELLED
  created_at  DATETIME NOT NULL,
  UNIQUE KEY uk_wait (activity_id, user_id, status),
  UNIQUE KEY uk_seq (activity_id, seq_no)
);

排队序号用同一活动的自增序号生成,谁先登记谁排前面。用户重复登记时靠唯一键幂等复用已有候补,而不是插入重复行。

登记接口流程:先按 activity_id + user_id 查有效候补 → 存在则直接返回原候补;不存在则插入 WAITING,seq_no 取该活动当前最大值加 1。并发重复插入由唯一键拦截,捕获冲突后改为查询已有记录。

seq_no 不跨活动复用,避免活动间序号比较产生歧义;查询候补列表时按 seq_no 升序,天然就是公平顺序。

二、名额释放怎么触发:取消事件加扫描兜底

名额释放来自取消报名、审核不通过、超时未确认三种情况。推荐用事件驱动 + 定时扫描兜底双通道:

取消、不通过、超时关单时,发出一条 seat_released 事件,携带活动 ID 与释放数量; 消费端收到事件,把该活动的候补状态推进到"可补位"; 定时扫描兜底:每小时把"名额有空余且存在 WAITING 候补"的活动捞出来补触发,事件丢了也能接上。

事件体至少包含 activity_id、release_qty、source(取消、不通过、超时)与事件 ID;消费者以事件 ID 建幂等表,重复投递只处理一次。扫描兜底与事件处理可能同时触发,处理函数必须幂等:对同一候补的重复邀约不会生成两条 OFFERED。

代码语言:sql
复制
-- 扫描兜底:有空余名额且有待补位的活动
SELECT w.activity_id, COUNT(*) AS waiting_cnt
FROM waitlist w
JOIN activity_seat s ON s.activity_id = w.activity_id
WHERE w.status = 'WAITING' AND s.available_qty > 0
GROUP BY w.activity_id;

三、按顺序补位:确认窗口与超时

补位不能"一释放就自动占用",因为候补用户可能已经不想参加。正确流程是:

按 seq_no 从小到大取候补 → 发起补位邀约(状态置 OFFERED)→ 给一个确认窗口(如 30 分钟)→ 用户确认则占用名额(CONFIRMED);超时未确认置 EXPIRED,名额继续释放给下一位。

确认窗口要单独记开始时间,超时由任务扫描触发,避免依赖用户主动操作。同一时刻只有一个候补处于 OFFERED 状态,防止两个候补同时占用一个名额。

代码语言:sql
复制
UPDATE waitlist
SET status = 'EXPIRED'
WHERE status = 'OFFERED'
  AND offered_at < DATE_SUB(NOW(), INTERVAL 30 MINUTE);

超时释放后名额回到空余池,由释放事件或下一轮扫描继续触发补位,链条不会断。

四、几百人同时候补、取消,怎么保证不重不漏

并发集中在两个点:多人同时取消释放名额;多人同时确认补位。分别处理:

名额侧:确认补位时用"条件更新"抢占——只有 available_qty > 0 才扣减,影响行数为 0 说明名额已被抢走,该候补重新进入等待; 候补侧:OFFERED → CONFIRMED 的流转用乐观锁,只允许本人、且状态为 OFFERED 的候补确认成功; 释放侧:同一活动的释放事件按活动 ID 串行消费,多实例部署时靠幂等表去重,避免同一取消事件触发两次补位。

代码语言:sql
复制
UPDATE activity_seat
SET available_qty = available_qty - 1
WHERE activity_id = #{activityId} AND available_qty > 0;

五、补位结果怎么通知:成功、失败、超时三种都要发

补位成功通知用户"已为你保留名额,请在窗口内确认";确认成功通知"报名成功";超时或确认失败通知"名额已释放,可重新报名"。通知按"用户 + 活动 + 批次"幂等去重,补位失败退回队列时不重复通知。

通知投递失败走重试队列,重试次数上限内按指数退避,超过上限转人工跟进。所有通知先写通知表再发送,状态可查,报名端按状态展示补位进度。

踩坑清单

  • 候补不建唯一索引,同一用户重复登记产生多条记录;
  • 排队序号用随机数或时间戳拼,顺序错乱;
  • 释放只靠事件不靠扫描,事件丢失后名额永远空着;
  • 补位直接占用名额不设确认窗口,候补用户不来也占着坑;
  • 确认补位不做条件更新,两个候补同时确认导致超发;
  • 补位通知不做幂等,失败重试与下一轮补位重复推送;
  • 确认窗口不记录开始时间,超时无从判定;
  • 释放事件不带事件 ID,重复投递触发多次补位。

工程落地建议

候补与补位本质是"有序队列 + 资源释放事件 + 确认窗口"三件事:排队用自增序号保证公平,释放用事件加扫描兜底保证不漏,占用用条件更新保证不超发。这套模式适用于报名、预约、限量领取等一切名额型业务。先建好候补表唯一约束,再逐步加确认窗口与并发保护。本文方案仅作技术分享,具体实现按自身业务取舍。

上线复盘清单

  • 候补表是否有活动加用户的唯一约束,排队序号是否自增有序
  • 名额释放是否有事件与定时扫描双重保障
  • 补位是否按顺序邀约,确认窗口与超时是否生效
  • 确认补位是否条件更新,是否保证不超发
  • 通知是否按用户、活动、批次幂等去重
  • 释放事件是否幂等消费,多实例是否只补位一次

常见问题

Q:报名名额满了之后,有人取消怎么按顺序补位?

A:候补按登记顺序排队,名额释放时从队首发起补位邀约,用户确认后占用名额,超时未确认则顺延给下一位。

Q:同一用户重复登记候补,会排几个位置吗?

A:不会。候补表按活动与用户建唯一约束,重复登记幂等复用原候补,排队序号不变。

Q:多人同时取消、同时确认,补位会重复或超发吗?

A:释放事件按活动串行消费并幂等去重,确认补位用 available_qty > 0 条件更新抢占,保证一个名额只被一个候补占用。

Q:候补用户一直不确认怎么办?

A:设确认窗口(如 30 分钟),超时由扫描任务置为过期并释放名额,顺延给下一位候补。

Q:候补队列会一直占着名额吗?

A:不会。候补不占正式名额,只有确认补位时才扣减 available_qty;超时未确认会释放并顺延给下一位。

结语

候补做不好,要么顺序不公平,要么补位重复超发。把有序队列、释放事件和条件更新立住,名额流转自然有序。仅作技术分享。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 一、候补先解决"谁能排上":登记表与唯一约束
  • 二、名额释放怎么触发:取消事件加扫描兜底
  • 三、按顺序补位:确认窗口与超时
  • 四、几百人同时候补、取消,怎么保证不重不漏
  • 五、补位结果怎么通知:成功、失败、超时三种都要发
  • 踩坑清单
  • 工程落地建议
  • 上线复盘清单
  • 常见问题
  • 结语
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档