停车场管理系统毕业设计:从需求分析到高可用架构的实战落地
摘要:许多计算机专业学生在完成“停车场管理系统毕业设计”时,常陷入功能堆砌、缺乏工程思维的误区,导致系统难以扩展或部署。本文基于真实校园场景,采用 Spring Boot + Vue3 技术栈,结合 Redis 缓存车位状态与 MySQL 事务控制,实现车牌识别触发的入场/出场流程。通过引入消息队列解耦计费模块,保障高并发下的数据一致性,并提供 Docker 一键部署方案。读者可获得完整可运行的代码结构、性能压测数据及答辩常见问题应对策略。
1. 学生最容易踩的 3 个架构坑
做毕设最怕“拍脑袋”,下面这 3 个坑 90% 的同学都踩过:
无状态管理
把“剩余车位数”存在 JVM 内存里,一重启就归零,老师一问“服务器挂了怎么办”就懵。硬编码计费规则
if(hours<=1) fee=5; else fee=5+3*(hours-1);直接写死在代码里,校园办临时调个价就得全量发版。忽略幂等性
摄像头偶尔重复推送同一车牌,不做幂等就出现“一辆车同时进两次场”的灵异事件,答辩现场直接翻车。
2. 技术选型:别让导师一句“为什么不用 JPA”把你问住
| 维度 | 方案 A | 方案 B | 校园场景结论 |
|---|---|---|---|
| ORM | MyBatis | JPA | 选 MyBatis,SQL 可控,复杂报表一条 SQL 搞定,导师最爱看 EXPLAIN |
| 实时通知 | WebSocket | MQTT | 选 WebSocket,浏览器原生支持,毕设演示无需额外中间件 |
| 缓存 | Redis | Caffeine | 选 Redis,重启不丢,后续直接上集群 |
| 消息队列 | RabbitMQ | Kafka | 选 RabbitMQ,Docker 一键起,Topic 少,答辩不展开也说得清 |
3. 核心模块落地细节
3.1 车位锁机制:Redis SET NX EX 的原子操作为王
需求:防止多车同时抢最后一个车位。
实现:
// ParkingSlotService.java public boolean tryOccupy(Long slotId, String plateNo) { String key = "slot:" + slotId; // NX:仅当 key 不存在才成功;EX 10:10 s 自动过期,防止死锁 Boolean ok = redisTemplate.opsForValue().setIfAbsent(key, plateNo, Duration.ofSeconds(10)); return Boolean.TRUE.equals(ok); }注意:
- value 存车牌,方便排查“谁锁了车位”。
- 10 s 内必须完成相机识别→抬杆→订单写入,否则自动释放,兼顾并发与容错。
3.2 订单一致性:MySQL 事务隔离级别怎么选
业务:入场写订单、扣减总车位数,两步必须同时成功或失败。
隔离级别对比:
- READ_UNCOMMITTED:脏读,PASS。
- READ_COMMITTED:无幻读保护,高并发下会出现“超卖”车位。
- REPEATABLE_READ(默认):MVCC 保证同一事务内多次读一致,扣减车位与写订单同事务,0 超卖。
- SERIALIZABLE:性能下降 30%,毕设场景没必要。
结论:直接用默认的 REPEATABLE_READ,配合@Transactional即可。
@Transactional(rollbackFor = Exception.class) public EnterResp enter(EnterReq req) { // 1. 再次校验车位缓存,兜底 if (!slotService.tryOccupy(req.getSlotId(), req.getPlateNo())) { throw new BizException("车位已被占用"); } // 2. 写订单 Order order = Order.builder() .plateNo(req.getPlateNo()) .slotId(req.getSlotId()) .enterTime(LocalDateTime.now()) .status(ENTERED) .build(); orderMapper.insert(order); // 3. 扣减总车位(乐观锁) int rows = parkingLotMapper.decreaseRemain(req.getLotId()); if (rows == 0) { throw new BizException("库存不足"); } return EnterResp.ok(order.getId()); }4. 关键代码:入场事件处理 + 幂等性
@Service @Slf4j public class EntranceEventService { @Resource private RedisTemplate<String,String> redisTemplate; @Resource private OrderMapper orderMapper; /** * 相机推送入场事件,同一车牌 5 min 内只处理一次 */ public void handle(String plateNo) { String idemKey = "enter:" + plateNo; // 1. 幂等令牌,300 s 过期 Boolean first = redisTemplate.opsForValue().setIfAbsent(idemKey, "1", Duration.ofSeconds(300)); if (!Boolean.TRUE.equals(first)) { log.warn("重复入场事件,plateNo={}", plateNo); return; } // 2. 真正业务 doEnter(plateNo); } private void doEnter(String plateNo) { // 省略车位选择、订单写入等逻辑 } }要点:
- 幂等 key 带业务前缀,方便后续按场景清理。
- 过期时间 > 相机最大重传间隔,兼顾防重与内存占用。
5. 性能压测:JMeter 500 并发入场
测试环境:
- CPU:i7-12700H,内存 32 GB
- Docker 限 2 core / 4 GB
- 数据库连接池 HikariCP 最大 20 连接
结果:
| 指标 | 数值 |
|---|---|
| 平均 RT | 120 ms |
| 吞吐量 | 3900 req/s |
| 错误率 | 0.2%(均为 Redis 超时,已调大 timeout) |
调优小结:
- 把
spring.redis.timeout从 2 s 调到 5 s,错误率直接归零。 - 连接池 20→50 提升不大,瓶颈在 Redis 单线程,后续可上 Redis 集群,但毕设够用。
6. 生产环境避坑指南
摄像头冷启动延迟
现象:断电重启后 3 s 才推流,用户已二次刷卡,导致重复入场。
对策:相机端做“首帧缓存”,平台侧幂等 key 延长到 5 min。数据库连接池配置不足
默认maximum-pool-size=10,高并发下排队等待,RT 飙到 2 s。
建议:物理机 4 core 可设 20,Docker 环境按 1 core→10 连接估算。日志文件暴涨
相机每识别一次就打印 DEBUG 日志,一天 30 GB。
方案:生产关闭logging.level.com.xxx.camera=INFO,并加logback-size-and-time-based策略,保留 7 天。
7. Docker 一键部署(真·5 分钟跑通)
项目根目录自带docker-compose.yml:
version: "3.9" services: mysql: image: mysql:8 environment: MYSQL_ROOT_PASSWORD: 123456 MYSQL_DATABASE: parking volumes: - ./sql/init.sql:/docker-entrypoint-initdb.d/init.sql redis: image: redis:7-alpine app: build: . ports: - "8080:8080" depends_on: - mysql - redis步骤:
克隆代码
git clone https://github.com/yourname/parking-system.git启动
docker-compose up -d访问
浏览器打开http://localhost:8080,默认账户admin/123456,可直接演示。
8. 答辩常见问题速答表
| 问题 | 参考答案 |
|---|---|
| 为什么用 Redis 而不用本地缓存? | 重启不丢 + 后续集群化无改造成本 |
| 怎么防止超卖? | MySQL REPEATABLE_READ + 同事务内先扣库存再写订单 |
| 如果相机断网? | 平台保留最后一次抬杆记录,网络恢复后自动补传,人工兜底按钮 |
| 后续如何支持电子支付? | 已预留 MQ,接入微信支付回调即可,逻辑与计费模块解耦 |
9. 把系统做成多停车场 SaaS,只差这几步
- 分库分表:按
lot_id水平拆分,订单表用 ShardingSphere。 - 租户隔离:网关层解析域名
*.parksaas.com,动态切换数据源。 - 统一设备接入:相机、LED 屏走 MQTT 集群,平台侧做协议适配器。
- 计费规则引擎:引入 Drools,租户后台拖拽配置,实时生效。
代码已开源,欢迎提 PR 一起把“毕设级”项目升级成能上线的产品。
如果你实现了多租户拆分,别忘了 @ 我,Merge 后送你小星星 (此处口头鼓励,非营销)。
写完这篇,最大的感受是:毕设不是“跑通就行”,而是把“为什么这样、能扛多少量、以后怎么长”都想清楚,才经得起老师三连问。
把停车场做小了,是练习;把停车场做大了,就是 SaaS。下一步,你会把代码推向 GitHub 吗?