新闻详情

新闻详情

首页 / 资讯中心 / 详情

学生选课系统课程设计:从E-R图到并发控制的完整数据库实现

发布时间:2026/10/2 6:56:34来源:尧图网络
学生选课系统课程设计:从E-R图到并发控制的完整数据库实现
简介这是一份数据库系统课程设计报告主题为“学生选课管理信息系统”完整呈现从需求分析、数据库设计到物理实现、应用开发的标准流程。报告前半部分围绕系统需求展开涵盖可行性分析、功能需求、组织结构、业务流与数据流分析并整理出数据项与数据结构字典中间部分依次完成实体、属性、联系分析绘制CDM概念模型再转化为PDM逻辑模型后半部分则聚焦SQL Server实践包括表结构设计、完整性约束代码、视图、触发器、存储过程与索引的创建以及学生信息、课程信息模块的调试过程最后给出登录模块和成绩管理模块的程序设计说明。整份报告遵循标准课程设计文档格式结构完整、内容详实适合计算机专业学生用作课程设计参考或毕业设计文档模板。资源包内含1个docx文档压缩后大小约3.96MB现已有851人学习下载打开即可查看完整可编辑内容便于替换表名、字段与页面信息后直接复用。1. 数据库系统课程设计报告学生选课管理信息系统在解决什么问题三个学生同时抢最后一个选课名额系统发出了两张选课成功的回执。这是把“先查余量再插入”写进学生选课管理信息系统后最容易翻车的现场。这个课设题目几乎把数据库系统课程的核心考点串完了E-R图设计、关系模式规范化、多表连接查询、事务与并发控制。你最后要交的不是一堆建表语句而是一个能查课、能选课、能退课、能出成绩单并在多人同时操作时不产生脏数据的小系统。这篇笔记按“表结构→业务SQL→并发排错→测试与备份→答辩自证”的顺序把可直接复现的写法讲清楚。适合不确定表怎么拆、并发控制无从下手、希望报告里多一些能演示模块的同学。2. 先拆E-R图再建表从成绩单反推六张表的理由与建表SQL2.1 三张表还是六张表教学班实体为什么不能省第一版通常只有学生表、课程表、选课表跑起来很顺畅直到出现一个需求同一门数据库系统概论有两个老师各开一个班。课程表里只有一条CS101选课表关联不到“哪个老师、哪个时段、哪个教室”成绩单打印出来也写不出任课教师。这类题在《数据库系统概论》的E-R图例题里经常出现解决方式就是引入“教学班”实体有的教材叫开课实体。教学班的属性包括开课编号、课程号、教师号、学期、上课时间、教室、容量、剩余名额。它和课程之间是多对一同一门课每学期开多个班和教师之间也是多对一一个教师可以带多个班。选课关系要跟着改成“学生选的是教学班而不是课程本身”这样同一个人重复选同一门课不同老师的班就具备了数据基础。有同学问教师表要不要单独建。建议单独建教师的工号、职称、院系放在教学班表里会造成冗余后续加“按教师查授课任务”的功能时还得拆表。课设规模下六张表足够学生、教师、课程、教学班、选课。若题目包含登录模块再加一张管理员表和核心业务分开不要让管理员账号混进学生表。一个课设里常见分歧是成绩字段放哪。常见做法是放在选课表里因为一次选课对应一条成绩记录成绩生命周期和选课记录一致单独建成绩表只会让退课多一张表要联删。报告里写清楚“选课表兼做成绩表”即可评分关注的是逻辑是否自洽。2.2 字段约束、主键外键与三个必建索引字段类型有几个点要注意。student_id用CHAR(10)而不是INT学号以0开头时会丢前导零。gender用CHAR(2)或TINYINT别用ENUMENUM在迁移数据时会带来预料之外的坑。offer_id用INT AUTO_INCREMENT这是教学班表的主键。remain用INT UNSIGNED但光约束非负还不够选课时要配合WHERE remain0才能挡住并发。semester用VARCHAR(20)存2024-2025-1这种格式不要用DATE学期不是自然日期跨年下学期也没法用DATE准确表达。索引不要只依赖主键。第一选课表的联合主键是(student_id, offer_id)它同时防止同一个人重复选同一个班但教务老师查看“某班选了哪些人”时最左前缀是student_id用不上offer_id前缀所以要给选课表的offer_id单独建索引。第二课程表按course_id和semester查询最多course_offer表上建(course_id, semester)联合索引。第三teacher_id也会被频繁关联补一个普通索引。外键约束要写明动作。选课记录被大量业务引用删除班级时选课记录应一并清理所以选课表外键用ON DELETE CASCADE课程和教师被教学班引用误删会破坏成绩单用ON DELETE RESTRICT拒绝删除。这个细节放进报告的物理设计章节比空谈“参照完整性”更有说服力。2.3 六张表的完整建表SQL与参数说明CREATE DATABASE IF NOT EXISTS course_select DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE course_select; CREATE TABLE student ( student_id CHAR(10) NOT NULL, student_name VARCHAR(20) NOT NULL, gender CHAR(2) NOT NULL DEFAULT 男, college VARCHAR(50) NOT NULL, enroll_year VARCHAR(9) NOT NULL, PRIMARY KEY (student_id) ) ENGINEInnoDB; CREATE TABLE teacher ( teacher_id CHAR(6) NOT NULL, teacher_name VARCHAR(20) NOT NULL, title VARCHAR(20), college VARCHAR(50) NOT NULL, PRIMARY KEY (teacher_id) ) ENGINEInnoDB; CREATE TABLE course ( course_id VARCHAR(10) NOT NULL, course_name VARCHAR(50) NOT NULL, credit DECIMAL(3,1) NOT NULL, hours INT NOT NULL, PRIMARY KEY (course_id) ) ENGINEInnoDB; CREATE TABLE course_offer ( offer_id INT NOT NULL AUTO_INCREMENT, course_id VARCHAR(10) NOT NULL, teacher_id CHAR(6) NOT NULL, semester VARCHAR(20) NOT NULL, class_time VARCHAR(50), room VARCHAR(30), capacity INT UNSIGNED NOT NULL, remain INT UNSIGNED NOT NULL, PRIMARY KEY (offer_id), KEY idx_offer_course_sem (course_id, semester), KEY idx_offer_teacher (teacher_id), CONSTRAINT fk_offer_course FOREIGN KEY (course_id) REFERENCES course (course_id) ON DELETE RESTRICT, CONSTRAINT fk_offer_teacher FOREIGN KEY (teacher_id) REFERENCES teacher (teacher_id) ON DELETE RESTRICT ) ENGINEInnoDB; CREATE TABLE enrollment ( student_id CHAR(10) NOT NULL, offer_id INT NOT NULL, enroll_time DATETIME DEFAULT CURRENT_TIMESTAMP, grade DECIMAL(5,1) DEFAULT NULL, PRIMARY KEY (student_id, offer_id), KEY idx_enroll_offer (offer_id), CONSTRAINT fk_enroll_student FOREIGN KEY (student_id) REFERENCES student (student_id) ON DELETE RESTRICT, CONSTRAINT fk_enroll_offer FOREIGN KEY (offer_id) REFERENCES course_offer (offer_id) ON DELETE CASCADE ) ENGINEInnoDB;字符集用utf8mb4而不是utf8MySQL里的utf8最多存3字节中文没问题但学生姓名里出现生僻字就可能入库失败utf8mb4才是完整Unicode。学分和成绩用DECIMAL不要用FLOAT浮点精度问题会导致87.5存成87.49999成绩单打印出来很难看。外键动作按上一小节的原则RESTRICT挡住误删课程和老师CASCADE让删除教学班时自动清理选课记录。注意course_offer表里的remain是统计冗余字段。没有它选课前要COUNT(*)一遍选课记录在并发下还要加锁才能保证不超卖。保留remain并配合条件更新是适合课设规模的方案这个选择会在第四章展开。3. 查课、选课、退课、成绩录入四条核心SQL的可复现写法3.1 选课把“先查后写”改成“条件更新”余量才不会超卖选课的业务逻辑看起来简单查余量余量大于0则插入选课记录再把余量减一。问题在于“查”和“减”是两条语句中间隔着一个应用层的判断并发场景下两个请求都能查到余量1然后各自扣减最终库存变成-1。最可靠的做法是让数据库把“检查余量”和“扣减余量”合并成一条原子语句。DELIMITER // CREATE PROCEDURE sp_enroll( IN p_student_id CHAR(10), IN p_offer_id INT ) BEGIN DECLARE v_rows INT DEFAULT 0; START TRANSACTION; UPDATE course_offer SET remain remain - 1 WHERE offer_id p_offer_id AND remain 0; SELECT ROW_COUNT() INTO v_rows; IF v_rows 0 THEN ROLLBACK; ELSE INSERT INTO enrollment(student_id, offer_id, enroll_time, grade) VALUES (p_student_id, p_offer_id, NOW(), NULL); COMMIT; END IF; END// DELIMITER ;调用示例CALL sp_enroll(2023001001, 2);选课的扣余量和插入选课记录必须放在同一个事务里否则会出现余量扣了但记录没插入的情况。ROW_COUNT()返回上一条UPDATE影响的行数等于1说明扣减成功且这次选课有效等于0说明名额已满。这个函数必须在UPDATE之后立刻使用中间不能夹着其他查询。这里传入的p_offer_id是教学班ID不是课程ID因为学生选的是“这个学期的这个老师的这个班”。如果同一个学生重复选同一个班INSERT会撞上联合主键(student_id, offer_id)并抛出1062错误。应用层要把1062当成“已选过”提示给用户并且整个事务要回滚否则会出现UPDATE扣了余量但选课记录没写入的情况。不要用“先SELECT判断是否已选没有就插入”的方式去避免1062并发下这个判断同样不可靠联合主键兜底加异常回滚才是稳妥路径。3.2 退课与改录成绩事务边界和禁用条件退课和选课逻辑对称但有一些细节。已录入成绩的课程不能退否则成绩单上凭空少一条记录成绩数据完整性被破坏。删除选课记录和回补余量同样要在同一事务里完成。DELIMITER // CREATE PROCEDURE sp_drop_course( IN p_student_id CHAR(10), IN p_offer_id INT ) BEGIN DECLARE v_rows INT DEFAULT 0; START TRANSACTION; DELETE FROM enrollment WHERE student_id p_student_id AND offer_id p_offer_id AND grade IS NULL; SELECT ROW_COUNT() INTO v_rows; IF v_rows 0 THEN ROLLBACK; ELSE UPDATE course_offer SET remain remain 1 WHERE offer_id p_offer_id; COMMIT; END IF; END// DELIMITER ;这里必须检查DELETE影响的行数。如果学生同时开了两个窗口把同一门课退两次第一次删除成功第二次DELETE影响0行但UPDATE remainremain1依然会执行余量被多加一个库存就错了。处理方式是v_rows0时直接ROLLBACK不执行回补。grade IS NULL条件保证已录成绩的退课请求不会生效。成绩录入也要限制权限不然任何教师都能给任何班录成绩。常见写法是通过JOIN把任课教师条件带进UPDATEUPDATE enrollment e JOIN course_offer co ON e.offer_id co.offer_id SET e.grade 88.5 WHERE e.student_id 2023001001 AND e.offer_id 2 AND co.teacher_id T10001;影响行数为0时可以统一提示“找不到该学生在该班的选课记录或你不是任课教师”。这个写法避免先用SELECT查出记录再在应用层判断归属把权限判断也放进了一条UPDATE里。3.3 查课表和成绩单多表连接的JOIN顺序与结果排序查询本学期可选课程是系统最高频的查询之一展示给学生的信息包括课程名、学分、教师、时间、教室、余量。SELECT c.course_id, c.course_name, c.credit, t.teacher_name, co.class_time, co.room, co.remain FROM course_offer co JOIN course c ON co.course_id c.course_id JOIN teacher t ON co.teacher_id t.teacher_id WHERE co.semester 2024-2025-1 AND co.remain 0 ORDER BY c.course_id, co.class_time;WHERE里先用semester等值条件过滤命中course_offer表上的(course_id, semester)联合索引前缀再JOIN课程表和教师表取展示字段。ORDER BY用course_id让同一门课的不同班级在界面里排在一起后面跟class_time是为了同班次按时间稳定排序。成绩单查询从选课表出发先用student_id过滤出某个学生的少数记录再往外JOIN其他表中间结果集最小。SELECT c.course_name, c.credit, co.semester, t.teacher_name, e.grade FROM enrollment e JOIN course_offer co ON e.offer_id co.offer_id JOIN course c ON co.course_id c.course_id JOIN teacher t ON co.teacher_id t.teacher_id WHERE e.student_id 2023001001 ORDER BY co.semester DESC, c.course_id;JOIN列上都建了索引执行计划不会出现全表扫描。ORDER BY用学期倒序让最新学期的成绩出现在最上面。报告里讲“驱动表先过滤小结果集”时用这个SQL作例子比贴概念好使。别在WHERE条件里对semester做函数处理比如WHERE YEAR(semester)2024一旦写了函数索引就失效了这个坑在后面还会再提。4. 并发选课把余量扣成负数隔离级别、锁与四条排查记录4.1 两个事务同时读到“还有1个名额”的翻车现场用两个MySQL窗口手工模拟并发比写一堆压测脚本更能说明问题。窗口A执行USE course_select; START TRANSACTION; SELECT remain FROM course_offer WHERE offer_id 2; -- 结果1 -- 停在这里不要提交窗口B执行同样的语句结果也是1。两个事务各自都认为“还有名额”然后都做INSERT和UPDATE最终remain会被扣两次变成-1而实际只有一个学生选上。这不是SQL语法问题是业务逻辑没有把“检查余量”和“扣减余量”做成原子操作。MySQL默认隔离级别REPEATABLE READ也挡不住这个情况因为快照读只是让事务内多次SELECT结果保持一致并不会阻止两个事务基于各自的快照同时通过“余量0”的判断。这就是为什么课设里不做并发控制答辩时容易被问住的点。4.2 READ COMMITTED与REPEATABLE READ隔离级别解决不了超卖典型的并发问题有脏读、不可重复读、幻读三种。脏读是读到了未提交的数据靠READ COMMITTED就能解决不可重复读是同一事务中两次读到不同值靠REPEATABLE READ解决幻读是事务中两次查询的记录集合不同需要SERIALIZABLE或间隙锁解决。很多同学把隔离级别当成超卖的救命稻草但超卖的本质是“先读后写”存在时间窗口隔离级别限制的是读的可见性并不限制“读到的值在判断之后被别人修改”。在REPEATABLE READ下两个事务快照里remain都是1各自判断通过后并行写入这就超卖了。真正让数据库串行化这个操作的是行锁和条件更新UPDATE命中一行时会加排他锁第二个事务的同一条UPDATE会阻塞直到第一个事务提交后拿到最新余量继续执行。如果余量已经为0第二个事务的AND remain0条件不成立影响行数为0事务回滚。答辩被问到“你用的什么隔离级别”回答默认REPEATABLE READ然后补充“关键路径用条件更新事务串行化扣减”比只会背隔离级别定义深刻得多。4.3 条件更新是乐观控制SELECT ... FOR UPDATE是悲观控制另一种教科书式的写法是悲观锁START TRANSACTION; SELECT remain FROM course_offer WHERE offer_id 2 FOR UPDATE; -- 应用层判断 remain 0 UPDATE course_offer SET remain remain - 1 WHERE offer_id 2; INSERT INTO enrollment(student_id, offer_id, enroll_time, grade) VALUES (2023001001, 2, NOW(), NULL); COMMIT;FOR UPDATE给命中的行加排他锁其他事务的读写都要等锁释放。这个写法的前提是SELECT必须带主键或索引条件否则InnoDB会锁整张表压测时一半请求都会锁等待超时。而且锁要一直持有到事务结束应用层判断期间锁不释放吞吐量比条件更新方案低一点。条件更新本质是乐观控制不先拿锁而是用UPDATE影响行数来判断是否成功。它不需要应用层做额外判断失败直接回滚代码更短。课设规模下两种方案都能跑通如果老师明确要求“演示锁”就用FOR UPDATE版本如果不要求条件更新更稳妥。报告里可以写“本系统采用条件更新避免悲观锁在应用层判断期间长时间持有锁”这话答辩时站得住。4.4 四条血泪排查记录现象、原因、解决先说结论课设里并发相关的坑大多是同一个根源——把“读取-判断-写入”拆成了三条应用层语句中间没有事务保护。第一条。现象用两个浏览器同时选最后一个名额数据库里remain变成-1。原因应用代码先SELECT remain再判断是否大于0再INSERT最后UPDATE四个动作没有放进同一个事务。两个请求都通过“remain0”的判断。解决把选课逻辑改成存储过程条件更新加事务。压测前造一条余量正好为1的教学班记录。第二条。现象存储过程执行后余量少了1但enrollment表里没有新增记录。原因UPDATE成功之后INSERT因为重复键1062报错应用层没有捕获异常也没有回滚事务就停在那里UPDATE的提交和INSERT的失败各走各的。解决INSERT放进同一事务捕获1062后统一ROLLBACK再在应用层做“是否已选过”的预检查来降低撞键概率。第三条。现象压测一开始就大量锁等待超时。原因FOR UPDATE语句没有走索引把整张course_offer锁了或者事务里在查完余量后又做了耗时操作才提交锁持有时间太长。解决先EXPLAIN看执行计划确认type不是ALL再检查事务里有没有把网络请求或文件读写塞在查锁和提交之间。第四条。现象加了触发器维护余量后选课反而报错统计表和业务表数据不一致。原因业务UPDATE扣了一次余量触发器里又扣了一次两条维护路径同时存在导致重复扣减。解决余量维护只保留一条路径要么用业务UPDATE要么用触发器选一个别两个都用。这条后面还会展开。5. 视图、存储过程与测试数据报告能加分又能被复现的三个模块5.1 用视图固定两个高频查询成绩单与选课统计视图的价值是把复杂的多表JOIN封装成一个逻辑表应用层查询只面对一张视图SQL短维护时也只需要改视图定义。CREATE VIEW v_grade_report AS SELECT e.student_id, s.student_name, c.course_id, c.course_name, c.credit, co.semester, t.teacher_name, e.grade FROM enrollment e JOIN student s ON e.student_id s.student_id JOIN course_offer co ON e.offer_id co.offer_id JOIN course c ON co.course_id c.course_id JOIN teacher t ON co.teacher_id t.teacher_id;查询成绩单时直接SELECT * FROM v_grade_report WHERE student_id 2023001001 ORDER BY semester DESC;另一个视图做开课统计给教务老师看每个班的容量、余量、已选人数CREATE VIEW v_offer_stats AS SELECT co.offer_id, c.course_name, t.teacher_name, co.semester, co.capacity, co.remain, co.capacity - co.remain AS enrolled_count FROM course_offer co JOIN course c ON co.course_id c.course_id JOIN teacher t ON co.teacher_id t.teacher_id;注意视图里不要写死某个学期比如WHERE semester2024-2025-1否则换学期要改视图定义。视图定义里只保留结构具体学年学期由外层查询条件传入。《数据库系统概念》里把视图归入数据抽象的范畴报告里写一句“通过视图对成绩单查询做数据抽象应用层不直接面对底层五张表的JOIN”能体现你对数据库系统原理的理解。5.2 用存储过程批量生成测试数据别手动一条条INSERT手工INSERT几十个学生和几百条选课记录太慢而且容易录错外键。存储过程批量造数的核心思路是用循环拼接学号用取模运算分配班级用条件表达式生成不同属性。DELIMITER // CREATE PROCEDURE sp_gen_students(IN p_num INT) BEGIN DECLARE i INT DEFAULT 1; DECLARE v_id CHAR(10); WHILE i p_num DO SET v_id CONCAT(2023, LPAD(i, 6, 0)); INSERT INTO student(student_id, student_name, gender, college, enroll_year) VALUES (v_id, CONCAT(学生, i), IF(i % 2 0, 男, 女), 信息工程学院, 2023); SET i i 1; END WHILE; END// DELIMITER ;调用一次生成30个学生CALL sp_gen_students(30);LPAD(i,6,0)把数字1变成000001再和2023拼接成10位定长学号。IF(i%20,男,女)让性别分布均匀。CONCAT(学生,i)的命名方便后续识别测试数据。课程、教师、教学班可以照同样思路生成注意外键顺序不能乱先造教师再造教学班最后造选课记录。批量选课也可以直接用存储过程DELIMITER // CREATE PROCEDURE sp_gen_enroll(IN p_student_count INT, IN p_offer_count INT) BEGIN DECLARE i INT DEFAULT 1; WHILE i p_student_count DO CALL sp_enroll(CONCAT(2023, LPAD(i, 6, 0)), 1 (i MOD p_offer_count)); SET i i 1; END WHILE; END// DELIMITER ;这里的MOD把学生轮流分到不同教学班。造数前先清空enrollment表避免撞联合主键积累一堆1062错误。测试数据不求大30个学生、10门课、3个学期、每个学期每门课一个班就足够演示所有功能。5.3 测试用例表、备份恢复命令与报告撰写顺序课程设计的测试部分别写“系统运行正常”这种空话用一张功能测试表把每个操作的输入输出列清楚答辩时老师扫一眼就心里有数。模块测试输入预期结果选课学生S001选班1余量大于0选课表新增记录余量减1选课学生S001重复选班1提示已选过余量不变退课学生S001退掉未录成绩的班1选课记录删除余量回补退课学生S001退掉已录成绩的班2拒绝退课数据不变并发选课两个会话同时抢最后一个名额一人成功一人提示名额不足备份恢复是报告里必有的模块命令别写成图形界面的鼠标操作。命令行给两个命令就够了mysqldump --single-transaction -u root -p course_select course_select_backup.sql mysql -u root -p course_select course_select_backup.sql--single-transaction参数在InnoDB下可以在不锁表的情况下做一致性备份报告里写这个参数比裸mysqldump显得懂行。恢复前确认数据库存在不存在先CREATE DATABASE。恢复后检查关键表行数SELECT COUNT(*) FROM student; SELECT COUNT(*) FROM course_offer; SELECT COUNT(*) FROM enrollment;行数对得上备份才算真验证过不是只跑了命令看一眼输出就结束。报告撰写顺序建议按标准课设模板走需求分析里的用例场景放前面概念设计给E-R图和关系模式逻辑设计写范式检查结果物理设计给表结构、索引和存储引擎选择实现与测试给存储过程、视图和并发测试记录这套顺序下来每章都有东西写不会出现“系统概述”凑字数的情况。6. 答辩追问触发器统计、索引失效与并发自证实验6.1 余量字段到底该不该用触发器维护老师看到remain这个统计冗余字段大概率会问为什么不用触发器自动扣减。常见的触发器方案长这样CREATE TRIGGER trg_enroll_ai AFTER INSERT ON enrollment FOR EACH ROW UPDATE course_offer SET remain remain - 1 WHERE offer_id NEW.offer_id AND remain 0;这个写法的坑很具体如果业务代码里已经有一条UPDATE course_offer SET remain remain - 1触发器再扣一次余量就被重复扣减了。而且AFTER触发器是在INSERT成功后执行如果余量已经是0触发器里的UPDATE影响0行但不会报错INSERT照样成功超卖问题依然存在。批量导入选课记录时还会逐条触发1000条记录就执行1000次UPDATE。更可靠的方案是用BEFORE INSERT触发器去检查余量并拒绝插入但BEFORE触发器只能定义在enrollment表上要读取course_offer的余量需要额外的SELECT锁的控制不如条件更新直接。课设里更稳妥的说法是余量维护放在选课存储过程的事务里用条件更新保证检查与扣减的原子性触发器只用来写日志表不碰核心库存字段。这个解释答出来老师通常不再追问。6.2 EXPLAIN自证一个导致索引失效的写法索引失效在答辩现场很好演示。给semester列建了索引跑下面这条查询EXPLAIN SELECT * FROM course_offer WHERE semester LIKE %2024%;EXPLAIN结果里type列是ALL说明走了全表扫描。因为LIKE以%开头索引树里无法定位前缀MySQL只能放弃索引。把查询改成前缀匹配EXPLAIN SELECT * FROM course_offer WHERE semester LIKE 2024-%;type列会变成range说明用上了索引范围扫描。这个对比实验的直接应用场景是课表查询应用层从下拉框拿到完整学期字符串查询条件写等值匹配就好不需要模糊匹配。如果将来要做模糊搜索绝不能把%直接拼在传入的参数前头。把EXPLAIN前后的截图放进报告的物理设计部分比大段文字解释有效得多。6.3 两个终端跑出阻塞截图比口头解释锁更有效最后一个技巧是并发自证不需要JMeter两个MySQL命令行窗口就能做。窗口A执行USE course_select; START TRANSACTION; UPDATE course_offer SET remain remain - 1 WHERE offer_id 2 AND remain 0;窗口B执行同一条UPDATE然后就会卡住不动这是InnoDB行锁在起作用。回窗口A执行COMMIT窗口B立刻返回“0 rows affected”说明第二个事务没有拿到可扣减的余量。把这两张截图放进报告的并发测试栏比写“我们使用了行锁”更有说服力。这个实验要注意节奏窗口A不要执行完就关掉连接断开会导致事务自动回滚窗口B也不要提前CtrlC那是主动终止等待看不看到阻塞效果。实验完两边都回滚或提交把库里数据恢复到初始状态。我每次交课设前都会把系统里三层外三层折腾一遍造数据造到接近容量开两个终端抢同一个班EXPLAIN看关键查询最后删库再用备份恢复一次。这些步骤不需要多新的工具但能帮你提前把并发问题从玄学变成确定至少不会在答辩现场第一次翻车。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

SSM校园网站项目从导入到改造:IDEA配置、数据库初始化与排错指南 2026/10/2 7:51:52

SSM校园网站项目从导入到改造:IDEA配置、数据库初始化与排错指南

很多人拿到一套“java_ssm61学院信息工程系校园网站”项目源码时,第一反应是双击解压,然后把整个文件夹直接拖进IDEA。结果要么满屏红叉,要么启动Tomcat后浏览器给你一张404,更有甚者项目起来了,登录页面却报数据库连不…

阅读更多 →
Skills Manager:统一管理54款AI编程工具的Agent技能 2026/10/2 7:51:45

Skills Manager:统一管理54款AI编程工具的Agent技能

1. 为什么我们需要一个“技能中枢”过去一年里,我陆续在五六个AI编程工具之间来回切换。Claude Code、Cursor、Windsurf、Cline、Roo Code、Aider……每换一个工具,我就要重新配置一遍Agent技能:把同一份代码审查规则复制到不同的配置目录&am…

阅读更多 →
WPS批量修改表格样式:从手动到VBA一键格式化 2026/10/2 7:51:39

WPS批量修改表格样式:从手动到VBA一键格式化

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

阅读更多 →
激光雷达三种测距方式对比:ToF、三角测距与FMCW选型指南 2026/10/2 7:51:39

激光雷达三种测距方式对比:ToF、三角测距与FMCW选型指南

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

阅读更多 →
Koopman算子实现非线性系统线性化MPC控制 2026/10/2 7:51:39

Koopman算子实现非线性系统线性化MPC控制

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

阅读更多 →
Orcad Allegro补丁本质是Windows系统兼容性工程 2026/10/2 7:51:38

Orcad Allegro补丁本质是Windows系统兼容性工程

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

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉