学生选课管理系统数据库设计与源码拆解:从建表到抢课并发实战
简介这是一套面向高校计算机专业学生与Java初学者的学生选课管理系统完整学习资料围绕数据库设计、Java Web开发与学生信息管理三大方向帮助读者理解一个真实教务类项目的实现思路。资源包共15个文件约8.56MB包含8张运行截图、3份说明文本、2个源码压缩包、1份设计文档和1个SQL数据库脚本覆盖从建库建表到项目部署的完整链路。系统采用Servlet、JSP、JDBC与MVC架构涉及学生表、课程表、选课表等核心表结构并实现注册登录、选课冲突检测、选课记录查询与成绩查看等功能同时兼顾数据备份、权限管理与性能优化等工程细节。配套的课堂笔记与视频教程可辅助理解项目结构运行截图直观展示界面布局数据库脚本便于快速还原环境。目前已有5368人学习下载适合作为课程设计、毕业设计或Java Web入门练手项目参考。1. 学生选课管理系统从建表到抢课一套能跑通的数据库与源码拆解每到选课季教务系统卡顿、课程名额秒没、选课结果和课表对不上几乎是每个学校都会遇到的场景。学生选课管理系统含数据库、源码这个标题说的就是把这套流程从零搭出来学生登录、浏览课程、提交选课、系统校验容量与时间冲突、写入选课记录、生成个人课表教师端和管理端再做审核与统计。它适合两类人一类是课程设计或毕业设计需要完整交付物的同学另一类是想练手「业务约束 事务 并发」的后端初学者。真正难的不是页面而是数据库约束和并发下的名额扣减。下面按建库、写接口、压测、排错、进阶的顺序把一套可复现的方案讲清楚。2. 数据库先立住五张表撑起选课的全部约束2.1 为什么是五张表而不是三张很多入门方案只建学生表、课程表、选课表三张跑起来没问题一加业务就崩。常见做法是拆成五张学生表、教师表、课程表、开课表教学班、选课记录表。关键区别在于「课程」和「开课」要分开——课程是「数据结构」这门课本身开课是「2024 秋 张三老师周三 3-4 节」这个具体教学班。容量、时间、教师都挂在开课表上而不是课程表上。这样同一门课可以开多个班选课冲突判断也有地方落。选课记录表是核心它必须带唯一约束防止同一个学生对同一个教学班重复选课。这个约束不是靠代码 if 判断而是靠数据库唯一索引兜底因为并发下代码判断会失效。2.2 建表 SQL 与字段含义-- 学生表只存身份信息不存选课状态 CREATE TABLE student ( id BIGINT PRIMARY KEY AUTO_INCREMENT, sno VARCHAR(20) NOT NULL UNIQUE, -- 学号业务唯一键 name VARCHAR(50) NOT NULL, password VARCHAR(100) NOT NULL, -- 存哈希不存明文 major VARCHAR(50), created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 教师表 CREATE TABLE teacher ( id BIGINT PRIMARY KEY AUTO_INCREMENT, tno VARCHAR(20) NOT NULL UNIQUE, name VARCHAR(50) NOT NULL ); -- 课程表课程元数据不含容量和时间 CREATE TABLE course ( id BIGINT PRIMARY KEY AUTO_INCREMENT, cno VARCHAR(20) NOT NULL UNIQUE, -- 课程号 cname VARCHAR(100) NOT NULL, credit DECIMAL(3,1) NOT NULL, -- 学分 dept VARCHAR(50) ); -- 开课表真正的教学班容量和时间冲突都看这里 CREATE TABLE section ( id BIGINT PRIMARY KEY AUTO_INCREMENT, course_id BIGINT NOT NULL, teacher_id BIGINT NOT NULL, term VARCHAR(20) NOT NULL, -- 学期如 2024FALL capacity INT NOT NULL DEFAULT 0, -- 容量上限 selected INT NOT NULL DEFAULT 0, -- 已选人数冗余字段 day_of_week TINYINT NOT NULL, -- 1-7 start_slot TINYINT NOT NULL, -- 第几节开始 end_slot TINYINT NOT NULL, -- 第几节结束 FOREIGN KEY (course_id) REFERENCES course(id), FOREIGN KEY (teacher_id) REFERENCES teacher(id), INDEX idx_term (term) ); -- 选课记录表唯一约束是防重复选的最后一道防线 CREATE TABLE enrollment ( id BIGINT PRIMARY KEY AUTO_INCREMENT, student_id BIGINT NOT NULL, section_id BIGINT NOT NULL, status TINYINT NOT NULL DEFAULT 1, -- 1 已选 2 已退 created_at DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_stu_sec (student_id, section_id), FOREIGN KEY (student_id) REFERENCES student(id), FOREIGN KEY (section_id) REFERENCES section(id) );逻辑说明section.selected是冗余计数目的是避免每次查已选人数都去enrollment表 count选课高峰期这个 count 会成为瓶颈。代价是要保证它和真实记录一致后面用事务解决。uk_stu_sec唯一键保证一个学生对一个教学班只有一条记录退课用status标记而不是删行方便审计。参数说明capacity和selected用 INT 足够别用 VARCHAR。day_of_week用 TINYINT 存 1-7start_slot/end_slot存节次冲突判断就是区间重叠比存字符串时间好算得多。2.3 时间冲突判断的 SQL 写法选课前要查这个学生已选的教学班里有没有和待选教学班时间重叠的。区间重叠条件是a.start b.end AND b.start a.end同一天才算冲突。-- 查某学生已选课程中与目标 section 时间冲突的记录 SELECT e.section_id FROM enrollment e JOIN section s ON s.id e.section_id WHERE e.student_id ? AND e.status 1 AND s.day_of_week ? -- 目标教学班的星期 AND s.start_slot ? -- 目标 end_slot AND ? s.end_slot; -- 目标 start_slot逻辑说明这条查询返回非空就说明冲突直接拒绝选课。注意status 1只算已选记录退课的记录不参与冲突判断否则学生退课后想换时间会被自己的历史记录挡住。3. 选课接口怎么写事务、行锁与幂等3.1 选课为什么必须放在一个事务里选课要同时做三件事检查容量、写选课记录、把section.selected加一。这三步必须原子完成否则会出现「记录写了但计数没加」或者「计数加了但记录没写」的脏数据。更麻烦的是并发两个学生同时抢最后一个名额如果先查selected capacity再更新两个请求都可能查到还有名额然后都写入超卖。常见做法是用「更新时带条件」把检查和扣减合并成一条原子语句UPDATE section SET selected selected 1 WHERE id ? AND selected capacity;这条语句在数据库层面是原子的返回受影响行数为 1 表示抢到名额为 0 表示已满。这一步是整个选课系统最关键的地方比任何应用层锁都可靠。3.2 完整选课逻辑的伪代码def enroll(student_id, section_id): with db.transaction(): # 开启事务 # 1. 原子占名额失败直接返回已满 rows db.execute( UPDATE section SET selected selected 1 WHERE id %s AND selected capacity, (section_id,) ) if rows 0: raise BizError(课程已满) # 2. 时间冲突检查 conflict db.query_one( SELECT 1 FROM enrollment e JOIN section s ON s.id e.section_id WHERE e.student_id %s AND e.status 1 AND s.day_of_week (SELECT day_of_week FROM section WHERE id %s) AND s.start_slot (SELECT end_slot FROM section WHERE id %s) AND (SELECT start_slot FROM section WHERE id %s) s.end_slot, (student_id, section_id, section_id, section_id) ) if conflict: raise BizError(时间冲突) # 抛异常事务回滚名额自动还回去 # 3. 写选课记录唯一键冲突说明重复选 try: db.execute( INSERT INTO enrollment (student_id, section_id, status) VALUES (%s, %s, 1), (student_id, section_id) ) except DuplicateKeyError: raise BizError(请勿重复选课) return 选课成功逻辑说明第 1 步先占名额第 2、3 步任何一步失败都抛异常事务回滚会把selected的加一撤销不会出现名额被白白占掉的情况。第 3 步依赖唯一键uk_stu_sec即使前面冲突检查漏了重复选也会被数据库拦下。参数说明db.transaction()的隔离级别建议用 READ COMMITTED配合UPDATE ... WHERE selected capacity的行锁足够应对选课场景。不要用 SERIALIZABLE会带来大量锁等待选课高峰期直接拖垮系统。3.3 退课与幂等退课要把status改成 2同时selected减一同样放在事务里UPDATE enrollment SET status 2 WHERE student_id ? AND section_id ? AND status 1; UPDATE section SET selected selected - 1 WHERE id ? AND selected 0;逻辑说明第一条的status 1条件保证重复退课不会重复减计数天然幂等。第二条的selected 0防止计数被减成负数。两条都受影响行数为 1 才算退课成功。4. 避坑与排查选课系统最容易翻车的五个地方4.1 超卖名额变成负数或超出容量现象压测时发现某个教学班selected大于capacity或者退课后计数对不上。原因用了「先 SELECT 查余量再 UPDATE 扣减」的两步写法并发下两个请求都读到旧值。解决改成UPDATE ... WHERE selected capacity单语句原子扣减用受影响行数判断成败不要先查后改。4.2 重复选课同一学生同一教学班两条记录现象学生点两次提交enrollment表出现两条相同记录。原因只靠应用层判断「是否已选」网络重试或双击就绕过了。解决enrollment表加UNIQUE KEY (student_id, section_id)插入时捕获唯一键冲突返回友好提示。数据库约束是最后一道防线不能省。4.3 时间冲突漏判跨节次区间算错现象学生选了周三 1-2 节又选上了周三 2-3 节系统没拦。原因冲突判断写成了start_slot start_slot这种相等判断没处理区间重叠。解决用a.start b.end AND b.start a.end判断重叠注意边界包含关系2 节和 2 节开始算冲突。4.4 计数与记录不一致selected 和实际记录数对不上现象section.selected显示 30但enrollment里只有 28 条status 1的记录。原因某次操作只更新了一边或者事务没包住。解决所有涉及selected的改动必须和enrollment的写入在同一个事务里另外写一个对账脚本定期用SELECT COUNT(*)校正selected作为兜底。4.5 选课高峰期数据库连接打满现象开放选课瞬间接口大面积超时日志显示连接池耗尽。原因每个选课请求都开事务、持锁连接被长时间占用。解决把冲突检查等只读查询放到事务外先做一遍预判减少事务内耗时连接池大小按数据库承载能力设不要盲目调大对同一学生的请求做应用层串行化避免自己和自己抢锁。5. 进阶把选课系统做成能扛住真实选课季的样子前面四章跑通的是功能正确这一章说怎么让它扛住真实流量。第一个技巧是「预占 异步落库」选课高峰期先用 Redis 的原子递减做名额预占DECR返回负数就说明满了成功后再异步写数据库。这样数据库压力从「每次选课一次事务」降到「批量落库」代价是要处理 Redis 和数据库的一致性常见做法是落库失败时补偿回滚 Redis 计数。第二个技巧是「冲突判断前置缓存」。把每个学生已选课程的时间段缓存在内存里选课时先在内存判断冲突通过后再进数据库事务。缓存失效策略按学生维度选课/退课成功后主动更新该学生的缓存。第三个技巧是压测验证。用脚本模拟 500 个学生抢 50 个名额检查最终selected是否恰好等于 50enrollment里status 1的记录数是否也是 50两者必须一致。这个对账是判断系统有没有超卖或漏记的最直接方法。# 用 ab 或 wrk 压测选课接口的示例 wrk -t8 -c200 -d30s --latency \ -s enroll.lua \ http://localhost:8080/api/enroll逻辑说明-t8是 8 个线程-c200是 200 个并发连接-d30s压 30 秒enroll.lua里构造带学生 ID 和教学班 ID 的 POST 请求。压完看selected和记录数是否一致比看 QPS 更有意义。参数说明并发数不要一上来就 200先从 50 开始观察数据库连接数和锁等待逐步加压。真实选课季的并发特征不是持续高压而是开放瞬间的尖峰所以更该关注「尖峰后系统能否恢复」而不是平均 QPS。我自己的习惯是任何涉及名额、库存、余额的功能第一反应不是写业务代码而是先想清楚「哪条 SQL 是原子的、哪个约束是兜底的、事务边界在哪」。选课系统看着简单把这三点想明白后面加功能才不会处处漏。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

老股民总结的8条避险与回本口诀,助你看清股市买卖时机

老股民总结的8条避险与回本口诀,助你看清股市买卖时机

摆脱追涨杀跌的困局在股市的博弈中,许多散户投资者常常深陷“一买就跌、一卖就涨”的怪圈。面对盘面的剧烈波动,缺乏系统交易框架的投资者极易被短期情绪左右,在盲目的追涨杀跌中不断蚕食本金,甚至错失原本可以解套回本的良机。作…

2026/10/11 13:50:10 阅读更多 →
职工信息管理系统数据库设计全流程:从需求分析到SQL Server建表实战

职工信息管理系统数据库设计全流程:从需求分析到SQL Server建表实战

简介:面向数据库课程设计学习者,最新职工信息管理系统数据库课程设计文档提供了一套可直接参考的完整方案,典型对应Java程序设计语言结合SQL SERVER 2021开发数据库应用系统的场景,适合计算机相关专业学生完成实践环节使用。文档按…

2026/10/11 13:50:10 阅读更多 →
Python+Vue宿舍管理系统全栈实战:Django与Flask选型、数据库建模及前后端联调

Python+Vue宿舍管理系统全栈实战:Django与Flask选型、数据库建模及前后端联调

1. 项目背景:为什么选了 PythonVue 来做宿舍管理系统 宿舍管理系统,几乎是每个做 Web 开发的人绕不开的“练手级”项目。学期末的课程设计、毕设选题、入职前的个人项目,翻来覆去都是这一类:人员管理、房间分配、报修登记、门禁记…

2026/10/11 13:49:10 阅读更多 →

最新新闻

进程地址空间深度剖析:虚拟地址转换、堆栈增长与内存问题定位

进程地址空间深度剖析:虚拟地址转换、堆栈增长与内存问题定位

写进程地址空间第一篇文章的时候,我把虚拟内存的整体框架拆开讲了一遍:从代码段到栈,从堆到内存映射段,把一张内存布局图硬生生画了半小时。文章发出后,有同学私信问我:既然地址空间只是个“虚拟”的概念&a…

2026/10/11 14:45:43 阅读更多 →
Excel多表合并三大方案:Power Query/VBA/Python实战指南

Excel多表合并三大方案:Power Query/VBA/Python实战指南

简介:本资源是一份面向Excel初学者与办公人员的实用VBA自动化教程,聚焦解决多工作表批量合并这一高频痛点问题。文档详细讲解了如何通过一段可直接运行的VBA代码,将多个结构一致的Excel工作簿(如各部门销售表、各门店日报等&#…

2026/10/11 14:45:43 阅读更多 →
Python协同过滤旅游推荐系统:从矩阵构建到线上验证的完整文档指南

Python协同过滤旅游推荐系统:从矩阵构建到线上验证的完整文档指南

简介:这份文档面向计算机相关专业学生与旅游推荐系统开发者,围绕“信息过载”下如何满足用户个性化出行需求展开,可作为毕业设计、课程设计或推荐算法入门项目的参考模板。压缩包内仅含1个docx文件,约8.64MB,内容为完整…

2026/10/11 14:45:43 阅读更多 →
深度学习100道选择题:知识图谱的压力测试仪

深度学习100道选择题:知识图谱的压力测试仪

简介:本资源是一套系统性的深度学习基础理论自测题集,面向人工智能初学者、高校学生及备考求职者,旨在帮助读者快速检验监督学习、无监督学习、模型评估、正则化、降维、目标检测等核心概念的掌握程度。题库共100道高质量单选题,覆…

2026/10/11 14:45:43 阅读更多 →
DSSS抗窄带干扰MATLAB仿真:从扩频增益到频域置零与半解析BER

DSSS抗窄带干扰MATLAB仿真:从扩频增益到频域置零与半解析BER

简介:这是一份面向无线通信学习者与MATLAB仿真实践者的DSSS扩频通信抗窄带干扰仿真代码包,聚焦直接序列扩频系统在窄带干扰环境下的建模与性能分析,适合通信工程、电子信息类专业学生及需要理解扩频抗干扰机制的开发者参考。包内共3个文件&am…

2026/10/11 14:45:43 阅读更多 →
单链表基础三题详解:删除节点、反转链表与找中间节点

单链表基础三题详解:删除节点、反转链表与找中间节点

链表这块内容,大学里第一次接触的时候觉得简单,无非是节点加指针。可真到动手写题的时候,删除节点能删丢一半,反转链表能绕成环,找中间节点还会因为奇偶数量吵半天。单链表综合练习里的“删除指定值节点”“反转链表”…

2026/10/11 14:44:43 阅读更多 →

日新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 10:45:37 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:53 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:54 阅读更多 →