SpringBoot+Vue3选课系统高并发架构实战
1. 项目概述现代选课系统的技术架构演进十年前我参与第一个选课系统开发时还是基于JSPServlet的单体架构每次选课季服务器必崩。如今这套SpringBootVue3MyBatis的前后端分离方案完美支撑了某高校3万师生同时在线选课的需求。本文将完整呈现从技术选型到具体实现的每个关键细节。选课系统的核心矛盾在于高并发时段的系统稳定性与业务逻辑的复杂性。传统方案往往顾此失彼而我们采用的这套技术栈通过以下方式破局前端Vue3的Composition API处理动态课表渲染SpringBoot的异步线程池应对选课高峰MyBatis的动态SQL实现多条件课表查询MySQL的乐观锁控制选课冲突2. 技术栈深度解析2.1 SpringBoot后端设计要点选课系统的SpringBoot工程采用多模块设计course-system ├── course-api // 接口定义 ├── course-biz // 业务逻辑 ├── course-dao // 数据访问 └── course-web // 控制器层关键配置类示例Configuration EnableAsync public class AsyncConfig implements AsyncConfigurer { Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor new ThreadPoolTaskExecutor(); executor.setCorePoolSize(20); // 选课核心线程数 executor.setMaxPoolSize(100); // 高峰时段扩容 executor.setQueueCapacity(500); // 等待队列 executor.setThreadNamePrefix(course-select-); executor.initialize(); return executor; } }重要提示线程池参数需要根据压测结果调整我们通过JMeter测试发现当并发超过800时MySQL连接池会成为瓶颈2.2 Vue3前端工程化实践前端采用Vue3VitePinia的技术组合目录结构如下src/ ├── api/ // 接口封装 ├── assets/ // 静态资源 ├── components/ // 公共组件 │ └── CourseTable.vue // 动态课表组件 ├── composables/ // 组合式函数 │ └── useCourseSelect.ts ├── router/ // 路由配置 └── stores/ // Pinia状态管理动态课表的核心实现script setup const columns reactive([ { key: mon, title: 周一 }, { key: tue, title: 周二 } // ...其他星期 ]) const fetchCourses async () { const { data } await api.get(/courses, { params: { semester: store.currentSemester, major: user.major } }) courses.value processCourseData(data) } /script2.3 MyBatis动态SQL实战课表查询需要处理多达12种筛选条件MyBatis的动态SQL完美应对select idselectCourses resultMapcourseResult SELECT * FROM courses where if testsemester ! null AND semester #{semester} /if if testcourseType ! null AND type #{courseType} /if !-- 其他条件 -- if testtimeRange ! null AND NOT EXISTS ( SELECT 1 FROM course_schedule WHERE course_id courses.id AND time_slot IN foreach itemslot collectiontimeRange open( separator, close) #{slot} /foreach ) /if /where ORDER BY credit DESC LIMIT #{offset}, #{pageSize} /select3. 核心业务实现细节3.1 选课并发控制方案我们对比测试了三种方案悲观锁SELECT FOR UPDATE性能差数据库唯一索引无法处理复杂约束乐观锁Redis原子操作最终方案具体实现代码public boolean selectCourse(Long studentId, Long courseId) { // Redis原子操作判断名额 Long remain redisTemplate.opsForValue() .decrement(course: courseId :quota); if (remain 0) { redisTemplate.opsForValue() .increment(course: courseId :quota); return false; } try { // 数据库乐观锁更新 int updated courseMapper.updateSelection( studentId, courseId, LocalDateTime.now(), // 使用版本号控制并发 getCurrentVersion(courseId)); return updated 0; } catch (Exception e) { // 回滚Redis计数 redisTemplate.opsForValue() .increment(course: courseId :quota); throw e; } }3.2 课表冲突检测算法核心冲突检测逻辑public boolean checkScheduleConflict(ListCourse selected, Course newCourse) { MapInteger, SetTimeSlot weekMap new HashMap(); // 转换已选课程时间 for (Course c : selected) { for (Schedule s : c.getSchedules()) { weekMap.computeIfAbsent(s.getWeekday(), k - new HashSet()) .addAll(expandTimeSlots(s)); } } // 检查新课程 for (Schedule s : newCourse.getSchedules()) { SetTimeSlot existing weekMap.get(s.getWeekday()); if (existing ! null) { for (TimeSlot newSlot : expandTimeSlots(s)) { if (existing.contains(newSlot)) { return true; } } } } return false; }4. 性能优化实战记录4.1 MySQL索引优化方案经过EXPLAIN分析我们为课程表设计了复合索引ALTER TABLE courses ADD INDEX idx_query ( semester, college_id, credit, capacity ); ALTER TABLE course_schedule ADD UNIQUE INDEX idx_course_time ( course_id, weekday, time_slot );4.2 前端性能提升技巧课表组件虚拟滚动template VirtualScroll :itemscourses :item-size60 template #default{ item } CourseItem :dataitem / /template /VirtualScroll /templateAPI请求防抖处理import { debounce } from lodash-es; const searchCourses debounce(async (keyword: string) { loading.value true; try { data.value await api.searchCourses(keyword); } finally { loading.value false; } }, 300);5. 部署架构与监控方案5.1 生产环境部署拓扑----------------- | CDN/OSS | ---------------- | ---------------------------------------------------------------- | Nginx (负载均衡) | | --------------------- --------------------- | | | SpringBoot节点1 | | SpringBoot节点2 | | | -------------------- -------------------- | | | | | | ----------v---------- ----------v---------- | | | Redis集群 | | MySQL主从 | | | --------------------- --------------------- | ----------------------------------------------------------------5.2 关键监控指标配置Prometheus监控配置示例- job_name: springboot metrics_path: /actuator/prometheus static_configs: - targets: [app1:8080, app2:8080] - job_name: mysql static_configs: - targets: [mysql-exporter:9104]Grafana监控看板包含选课成功率API响应时间P99MySQL活跃连接数Redis内存使用率6. 典型问题排查实录6.1 选课超时问题排查现象选课高峰期出现5%的请求超时排查过程发现线程池满日志Thread pool is EXHAUSTED检查MySQL连接池使用率高达90%发现课程查询SQL缺少索引定位到CourseSchedule表全表扫描解决方案-- 添加覆盖索引 ALTER TABLE course_schedule ADD INDEX idx_course_query ( course_id, weekday, classroom_id );6.2 内存泄漏问题处理通过Arthas排查步骤# 1. 监控堆内存 dashboard -i 5000 # 2. 分析堆对象 heapdump /tmp/heap.hprof # 3. 定位到课程缓存未清理 ognl com.example.CacheManagerinstance.cacheMap.size()最终发现是课程详情缓存未设置TTL添加Redis过期配置Bean public RedisCacheManager cacheManager(RedisConnectionFactory factory) { RedisCacheConfiguration config RedisCacheConfiguration .defaultCacheConfig() .entryTtl(Duration.ofHours(2)) // 设置2小时过期 .disableCachingNullValues(); return RedisCacheManager.builder(factory) .cacheDefaults(config) .build(); }7. 安全防护方案7.1 防刷课机制实现滑动窗口限流算法public boolean allowSelection(Long studentId) { String key limit: studentId; Long now System.currentTimeMillis(); // 移除1分钟外的记录 redisTemplate.opsForZSet().removeRangeByScore( key, 0, now - 60_000); // 添加当前请求 redisTemplate.opsForZSet().add( key, UUID.randomUUID().toString(), now); // 检查1分钟内请求次数 return redisTemplate.opsForZSet() .zCard(key) 30; // 每分钟不超过30次 }7.2 SQL注入防护始终使用MyBatis参数化查询!-- 错误示范 -- select idfindCourses SELECT * FROM courses WHERE name LIKE %${name}% /select !-- 正确做法 -- select idfindCourses SELECT * FROM courses WHERE name LIKE CONCAT(%,#{name},%) /select启用MyBatis安全审计插件Intercepts({ Signature(type StatementHandler.class, methodprepare, args{Connection.class,Integer.class}) }) public class SqlAuditInterceptor implements Interceptor { Override public Object intercept(Invocation invocation) { StatementHandler handler (StatementHandler) invocation.getTarget(); String sql handler.getBoundSql().getSql(); if (sql.matches(.*(drop|alter|truncate).*)) { throw new SecurityException(危险SQL操作); } return invocation.proceed(); } }8. 项目演进方向在实际运行中我们还规划了以下优化方向选课结果实时推送改用WebSocket替代轮询分布式事务方案引入Seata处理跨服务选课弹性扩缩容基于K8s的HPA自动扩缩容智能推荐基于学生历史数据的课程推荐算法特别分享一个性能调优经验在课程查询接口添加二级缓存后QPS从1200提升到6500。关键实现是使用Caffeine作为本地缓存Redis作为分布式缓存通过发布订阅机制保持各节点缓存一致性。

相关新闻

Dify实战指南:一周内搭建生产级AI应用,从RAG到智能体全掌握

Dify实战指南:一周内搭建生产级AI应用,从RAG到智能体全掌握

最近在尝试将AI能力集成到业务系统时,发现从零构建一个稳定、可扩展的AI应用链路异常复杂:模型选型、API集成、知识库构建、工作流编排、应用部署……每个环节都充满挑战。而Dify的出现,恰好提供了一个开箱即用的解决方案,它通过可…

2026/7/28 11:48:20阅读更多 →
3步终极解决方案:彻底解锁B站视频离线自由,永久保存4K大会员专属内容

3步终极解决方案:彻底解锁B站视频离线自由,永久保存4K大会员专属内容

3步终极解决方案:彻底解锁B站视频离线自由,永久保存4K大会员专属内容 【免费下载链接】bilibili-downloader B站视频下载,支持下载大会员清晰度4K,持续更新中 项目地址: https://gitcode.com/gh_mirrors/bil/bilibili-downloade…

2026/7/28 11:48:20阅读更多 →
上门按摩平台订单越多利润越薄?破局关键在于跳出单一抽佣思维

上门按摩平台订单越多利润越薄?破局关键在于跳出单一抽佣思维

最近在做上门按摩项目交流的圈子里,有一个反直觉的现象引发了广泛讨论:很多老板发现,平台每天的单量明明在涨,但月底一算账,利润却没怎么涨,甚至有的还在亏钱。 这其实暴露了当前行业的一个核心痛点——盈利…

2026/7/28 11:48:20阅读更多 →
通过全局选项实现个性化设置

通过全局选项实现个性化设置

为了适应个人偏好的多样性,满足每个用户计算硬件的不同规格,VirtualLab Fusion提供的工具可以根据您的个人情况进行个性化配置。下面我们演示如何在全局选项中对两组不同系统进行个性化设置:其一是,与数据的图形显示和一般可视化有…

2026/7/28 16:43:52阅读更多 →
毕业设计全流程解决方案:从选题到答辩

毕业设计全流程解决方案:从选题到答辩

1. 毕业设计全流程解决方案解析最近在整理工作室的接单记录时,发现毕业设计季的需求量呈现爆发式增长。很多同学在面临毕业设计时常常陷入迷茫——从选题到实现,从文档撰写到答辩准备,每个环节都可能成为拦路虎。作为经历过上百个毕业设计项目…

2026/7/28 16:43:52阅读更多 →
SMT生产中的UPPH:定义、计算与提升策略

SMT生产中的UPPH:定义、计算与提升策略

1. UPPH的定义与行业背景在SMT(表面贴装技术)生产车间里,UPPH这个缩写词几乎每天都会被产线主管、工艺工程师和IE(工业工程)人员挂在嘴边。作为衡量生产效率的核心指标,UPPH全称是Units Per Person per Hou…

2026/7/28 16:43:52阅读更多 →
Arduino Uno按钮控制LED:PinPong库入门与数字I/O交互原理详解

Arduino Uno按钮控制LED:PinPong库入门与数字I/O交互原理详解

1. 项目概述:从“点灯”到“交互”的硬件入门如果你刚开始接触硬件编程,或者玩过Arduino但一直停留在让LED闪烁的“Hello World”阶段,那么这个项目就是你从“单向输出”迈向“双向交互”的关键一步。项目标题“【pinpong库控制硬件】之Ardui…

2026/7/28 16:43:52阅读更多 →
VirtualLab Fusion中可视化的个性化设置

VirtualLab Fusion中可视化的个性化设置

摘要VirtualLab Fusion中的全局选项对话框可以轻松自定义软件的外观和风格,也可以保存和加载全局选项文件,以便可以轻松地将首选设置从一个设备转移到另一个设备。本文档演示了与结果的可视化和图形显示相关的全局选项参数的用法。如何访问全局选项转到主…

2026/7/28 16:43:52阅读更多 →
SpringBoot+Vue构建问卷调查系统的技术实践

SpringBoot+Vue构建问卷调查系统的技术实践

1. 项目概述这个基于SpringBootVueMySQL的问卷调查系统是一个典型的毕业设计项目,它完整实现了问卷创建、发布、填写和统计分析的闭环流程。作为前后端分离架构的实践案例,它既包含了基础CRUD功能,又涉及了数据可视化、权限控制等进阶特性&am…

2026/7/28 16:41:52阅读更多 →
覆盖国产 + 海外 + 开源模型,OpenClaw 2.7.9 Windows/Mac 双端部署详解

覆盖国产 + 海外 + 开源模型,OpenClaw 2.7.9 Windows/Mac 双端部署详解

🔹 工具基础介绍 OpenClaw 是开源生态中一款实用性较强的本地智能工具,凭借本地离线运行、可视化图形操作和任务自动化三大核心特性,赢得了众多用户的青睐。与普通在线对话AI工具不同,它属于能够直接操控本机软硬件的智能数字员工…

2026/7/28 4:06:39阅读更多 →
伺服阀焊完微漏毁整机?精密激光焊接三关锁住高压

伺服阀焊完微漏毁整机?精密激光焊接三关锁住高压

所谓液压伺服阀体的精密激光焊接,是用激光束对阀座壳体(通常为不锈钢或铝合金)进行密封焊接,使阀体在21-35MPa的高压液压油或压缩气体中长期运行而不发生介质泄漏。液压伺服阀是高端液压系统的"大脑"。从航空航天飞行控…

2026/7/28 2:08:06阅读更多 →
D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南

D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南

D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南 【免费下载链接】d2dx D2DX is a complete solution to make Diablo II run well on modern PCs, with high fps and better resolutions. 项目地址: https://gitcode.com/gh_mirrors/d2/d2dx 你是否还在…

2026/7/28 1:38:28阅读更多 →
告别臃肿!3步让你的暗影精灵笔记本重获新生

告别臃肿!3步让你的暗影精灵笔记本重获新生

告别臃肿!3步让你的暗影精灵笔记本重获新生 【免费下载链接】OmenSuperHub Control Omen laptop performance, fan speeds, and keyboard lighting, and unlock power limits. 项目地址: https://gitcode.com/gh_mirrors/om/OmenSuperHub 你是否也曾为官方Om…

2026/7/28 0:00:29阅读更多 →
RAG必踩坑!财报法规检索不准?这款开源工具让答案浮出水面,准确率飙升98.7%!

RAG必踩坑!财报法规检索不准?这款开源工具让答案浮出水面,准确率飙升98.7%!

做 RAG 的人应该都踩过这个致命的坑:把几百页的财报、法规、技术手册扔给向量库,问一个具体问题,搜出来的全是沾边但没用的内容 —— 关键信息要么被硬切块拆碎了,要么藏在几十条结果的最下面。语义相似≠真正相关,这个…

2026/7/28 0:00:29阅读更多 →
抖音视频文案提取工具全指南:免费2026版、手机App、在线工具一网打尽

抖音视频文案提取工具全指南:免费2026版、手机App、在线工具一网打尽

2026年做短视频运营,从抖音上扒文案早就不是偷偷抄笔记的事了。我刚开始做内容的时候,每天刷半小时抖音,手动把爆款视频的口播敲进备忘录,一条2分钟的视频得花十来分钟,碰到语速快的还要反复回听。后来试了一圈工具&am…

2026/7/28 0:00:29阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/27 16:57:54阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/28 3:17:03阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/28 2:35:58阅读更多 →