一场活动名额 50 个,第 51 个报名者怎么办?直接拒绝会损失用户,登记候补又怕补位顺序乱、同一人重复通知、几个人同时取消时补位打架。读完你能拿到一套"候补登记 + 释放事件 + 按序补位 + 确认窗口"的设计,附表结构与并发写法。队列怎么排、名额谁释放、补位怎么确认,三步立住,几百人同时操作也不乱。
候补的本质是"名额释放后的按序补位",第一步是把候补关系登记清楚,一个活动、一个用户只允许一条有效候补:
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。
-- 扫描兜底:有空余名额且有待补位的活动
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 状态,防止两个候补同时占用一个名额。
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 串行消费,多实例部署时靠幂等表去重,避免同一取消事件触发两次补位。
UPDATE activity_seat
SET available_qty = available_qty - 1
WHERE activity_id = #{activityId} AND available_qty > 0;补位成功通知用户"已为你保留名额,请在窗口内确认";确认成功通知"报名成功";超时或确认失败通知"名额已释放,可重新报名"。通知按"用户 + 活动 + 批次"幂等去重,补位失败退回队列时不重复通知。
通知投递失败走重试队列,重试次数上限内按指数退避,超过上限转人工跟进。所有通知先写通知表再发送,状态可查,报名端按状态展示补位进度。
候补与补位本质是"有序队列 + 资源释放事件 + 确认窗口"三件事:排队用自增序号保证公平,释放用事件加扫描兜底保证不漏,占用用条件更新保证不超发。这套模式适用于报名、预约、限量领取等一切名额型业务。先建好候补表唯一约束,再逐步加确认窗口与并发保护。本文方案仅作技术分享,具体实现按自身业务取舍。
Q:报名名额满了之后,有人取消怎么按顺序补位?
A:候补按登记顺序排队,名额释放时从队首发起补位邀约,用户确认后占用名额,超时未确认则顺延给下一位。
Q:同一用户重复登记候补,会排几个位置吗?
A:不会。候补表按活动与用户建唯一约束,重复登记幂等复用原候补,排队序号不变。
Q:多人同时取消、同时确认,补位会重复或超发吗?
A:释放事件按活动串行消费并幂等去重,确认补位用 available_qty > 0 条件更新抢占,保证一个名额只被一个候补占用。
Q:候补用户一直不确认怎么办?
A:设确认窗口(如 30 分钟),超时由扫描任务置为过期并释放名额,顺延给下一位候补。
Q:候补队列会一直占着名额吗?
A:不会。候补不占正式名额,只有确认补位时才扣减 available_qty;超时未确认会释放并顺延给下一位。
候补做不好,要么顺序不公平,要么补位重复超发。把有序队列、释放事件和条件更新立住,名额流转自然有序。仅作技术分享。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。