题目重点:区分书目与实体副本,保证借阅区间不重叠,用事务和 Outbox 连接还书与预约通知。
题意与假设
这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。
- 书目Book对应一个版次/ISBN,Copy对应带唯一barcode的一本实体书。同一书目可有多个副本,不能锁整本ISBN禁止所有副本借出。
- 训练合同一次借阅占用一个副本,预约先按书目/馆排队,在副本可用时生成带期限hold;题目没有罚款和跨馆物流规则。
- 预约领取与现场直接借书谁优先必须写政策,本文优先有效hold;历史时间使用半开区间[start,end)。
解题路线
- 模型Book、Library、Copy、Member、Loan、Reservation分开。Copy保存状态与version,Loan保存借出/归还时间和用户;Reservation保存队列次序、馆、状态和hold期限,避免把所有历史塞进Copy。
- search支持书名作者全文检索;availability按书目/馆索引查副本状态。搜索缓存可略迟,但borrow必须访问权威库存并做条件更新,不能从缓存决定成功。
- borrow在事务内验证用户、有效hold、Copy可借状态,将Copy改LOANED并创建Loan。唯一的active loan约束及行锁/条件写守住同副本至多一个有效借阅;逻辑上要求无区间重叠,数据库执行不能只靠先查历史后插入。
- return按loan_id幂等执行:设置归还时间、转Copy状态、领取下一个预约生成hold,并在同一事务插入Outbox事件。事务提交后异步通知;通知失败重试,已完成还书不回滚。
- hold领取条件更新为已使用;过期处理按version取消并给下一人,防止扫描器和领取请求竞态。把丢失、损坏和馆际转运作为不可借状态,不因为有借阅end时间就直接available。
- 大规模把馆/副本库存作为写分区,跨馆搜索读索引;用户借阅历史可做独立读模型但不能替代副本的权威借出状态。跨馆预约分配牵涉政策和物流,先定责任再加分布式事务。
手动走一遍
训练例同ISBN在馆A有副本a1、a2,用户U/V可以同时借不同副本。两人同时借a1时只有一个事务成功。U还a1后,预约用户W获得hold,通知故障由Outbox重试;V不能因为消息未送达抢借a1。
规模与取舍
库存条件更新单副本O(1),按ISBN/馆索引查可用副本约O(log C+输出量)。通知至少一次投递,consumer按事件id去重;年度历史量按借阅次数估,与书目数量分开。
易错点与追问
容易失分的边界
- ISBN作为实体书id,误阻塞其他副本。
- 先查available后无事务创建loan。
- 还书成功但通知失败就回滚,形成实体与软件状态矛盾。
- hold过期job覆盖刚刚领取成功的状态。
- 只设计CRUD,没说明借取还实际状态变化。
继续深挖
- 允许未来时段预订时,怎样保证半开区间不重叠?
- 馆际转运丢失副本,预约如何补偿?
- 用户重复点击还书,API返回什么?
- 读缓存显示可用但borrow失败,界面如何解释?
