工程约 3 分钟
50 借 + 50 还:把「不超借」写成并发断言的借还引擎
借书锁父表、还书锁子表会形成循环等待,高并发抢热门教材时连接池被打爆并出现负库存超借。记录我为什么选择统一加锁顺序而不是乐观锁,以及三个并发硬断言如何让结论可验证、可回归。
- 并发
- 死锁
- 数据库
- 测试
在这个校园图书借阅平台里,借还流通是我必须先把结论钉死的一条链路。它看起来只是两条普通的写操作,但借书与还书各自持有不同的锁 —— 顺序一旦不一致,两条链路就会互相等待。下面的判断与取舍都来自项目记录(问题、决策、断言与指标);超出记录的解释会标注为「分析」。
背景:为什么会担心并发
场景很具体:选课周、考试季,大量学生同时抢热门教材。同一批副本会被几十个请求同时争抢,而借还与归还的链路各自持有不同的锁。
问题:借书锁父表、还书锁子表
- 循环等待:借书锁父表、还书锁子表,两个事务以相反的顺序取锁就会互相等待。
- 连接池被打爆:等待中的事务占着连接不放,请求越堆越多。
- 负库存超借:更糟的是结果 —— 库存被扣成负数,同一本书借给了超出的读者。
我没有把它当成「SQL 写得对不对」的问题,而是当成加锁顺序的问题:循环等待正是死锁的四个必要条件之一。能直接破坏一个必要条件,就不该只在事后做补偿。
候选方案:三个选项
| 方案 | 结果 | 当时的判断依据 |
|---|---|---|
| 改用乐观锁 + 重试 | 未采用 | 属于「事后重试」;而循环等待可以直接被破坏,比事后补救更彻底 |
| 用单机同步锁包住整段逻辑 | 未采用 | 依赖单机状态,不能作为并发正确性的基础 |
| 统一加锁顺序(行级悲观排他锁) | 采用 | 所有事务按同一顺序取锁,从根上破坏循环等待 |
实现:把加锁顺序变成全局约定
我最终选择统一加锁顺序:借书与还书两条链路的加锁顺序被统一成同一个确定顺序,所有事务都按这个顺序取行级排他锁。顺序一致时,两个事务不可能再形成「你等我、我等你」的环。
验证:三个并发硬断言
这条链路上我最想保留的一个习惯是:不把并发结论写成描述性文字,而是写成用例里的硬断言。因为并发问题靠描述说不清,只有断言能在回归时报警。
| 并发场景 | 断言结果 | 测量方式 |
|---|---|---|
| 50 借 + 50 还交叉争抢同一批副本 | 超借数 = 0 | 后端并发用例断言(不是描述性文字) |
| 50 线程抢同一册副本 | 库存不足拦截数 = 恰好 49 | 后端并发用例断言 |
| 100 线程预约同一本孤本 | 排位覆盖 1–100,且互不重复 | 后端并发用例断言 |
同一批验证里还测了什么
| 项目 | 结果 | 测量方式 |
|---|---|---|
| 后端测试 | 248 / 248 通过(61 个测试类,0 失败 / 0 错误 / 0 跳过) | 解析 surefire-reports 汇总核对 |
| 前端测试 | 96 / 96 通过(23 个测试文件),flutter analyze 无问题 | flutter test 与 flutter analyze |
| 数据库迁移 | Flyway V1–V17 连续迁移无偏差 | 迁移历史核对 |
| 生产镜像体积 | 后端 150 MiB / 前端 39 MiB(多阶段构建、非 root 运行) | 构建产物 |
| 线上探针 | 首页 HTTP 200;/actuator/health 返回 UP;未带 token 的业务接口返回 401(中文错误信息 + traceId) | 2026-09-23 实测 |
同一条思路在项目里的另外两处
- 预约闭环:用归还事件驱动的 FIFO 状态机管理孤本预约,回收时主动唤醒队首读者,逾期未取自动淘汰并顺延 —— 资源不在队列里僵死。
- 推荐的可借性:把真实在架库存作为推荐权重的一路,并过滤无库存书目。这条线我坚持一个判断:算法再准,推荐了借不到的书,用户感受到的就是功能坏了。
复盘
- 我从此更愿意把结论写成断言,而不是写在文档里 —— 文档不会在回归时报警。
- 能消除的问题,不要只做补偿:这也是我倾向「破坏条件」而不是「事后重试」的原因。
- 代价要提前写下来:锁顺序是全局约定,这一点在评审里必须被当成硬规则。
完整的架构(确定性锁顺序、FIFO 预约状态机、库存感知的推荐、双模 AI 导读)与全部实测数据在项目页里:查看校园图书借阅系统项目详情。
韦坤计算机科学本科生 · 后端开发 / AI 应用开发方向
相关项目
面向高校图书馆的全栈借阅平台:用确定性锁顺序消除并发死锁、用 FIFO 闭环状态机管理孤本预约、用库存感知的多路加权做推荐,后端 248 + 前端 96 用例,含 3 个并发硬断言。
- Java 17 字节码(生产镜像 JDK 21,分代 ZGC)
- Spring Boot 3.3.4
- Spring Security + JWT(RBAC)
- 还有 26 项技术