题目重点:设计拍卖创建、竞价、关闭与支付,解释最高价原子更新、事件顺序和推送连接。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 训练是英式升价拍卖,出价必须大于当前最高价与最低步长;截止时刻以服务器权威时钟判定。是否自动延期、撤价和底价需题面明确。
- 核心不变量:关闭后不再接受普通出价;同一拍卖只有一个胜出结果,支付是后续独立状态而非必定成功。
- 容量假设热门拍卖1万出价/秒、100万观众;绝大多数观众只读,出价冲突与广播扇出是两个瓶颈。
解题路线
- 数据库保存auction、bid、winner/order与revision。POST bid带requestId;在按auction串行的事务或single-writer分区中检查开放状态、截止、价格,写bid并更新最高出价与revision,再写outbox。
- 以(serverAcceptedAt,sequence)定义接受顺序,不能以客户端时间或网络先后猜赢家。序列化更新允许明确回答两个同时出价谁成功;服务分片按auctionId,超热门单拍卖是不可随意分散的串行热点。
- 关闭任务幂等CAS OPEN→CLOSED并固化winner,扫描补偿漏掉的定时任务;不要依赖唯一一条内存timer。若支持软关闭延期,timer携带endTimeRevision防止旧timer提前关拍。
- 广播bidAccepted带sequence,用SSE提供单向更新;WebSocket适于额外交互,poll简单但延迟与请求量高,longpoll增加连接周转。观众重连提供lastSequence,超保留窗口回读当前快照。
- 付款失败、保留金、递补赢家和退款规则单独建订单状态机;高价显示不代表已成交。鉴权、反滥用和匿名展示须不泄露完整支付身份。
手动走一遍
最高价100,A出110/B出105同时到来:若A先被权威接受,B随后因低于110失败;若B先接受后A再接受,两次事件都合法且最终A领先。关闭revision必须与已接受bid序列对齐。
规模与取舍
单拍卖的出价事务串行成本决定极限TPS;读可从事件缓存广播。1万事件/秒×100万观众会产生100亿deliveries/秒,不能假装逐条全量发送可行:合并最高价更新、限频并区分逐笔审计订阅。
易错点与追问
容易失分的边界
- 在缓存读最高价再异步写数据库,两个出价可能都用旧价检查并互相覆盖。
- 截止后才收到的客户端提前出价是否接受,需要业务规则;不能接受客户端可伪造时间戳。
- 数据库成功但推送失败不回滚出价,通过outbox补发,客户端用sequence去重。
继续深挖
- 如何让最近出价自动延长一分钟?将延期与bid在同一原子更新,关闭任务读最新版本。
- 竞价分区leader故障后如何恢复唯一顺序?用持久日志与epoch fencing,旧leader不能继续写。
