资讯详情

数据库课程设计外卖点餐系统:MySQL建模到事务并发避坑全解析

📅 2026/10/11 20:59:16 | 华诺云谱 👁 阅读
数据库课程设计外卖点餐系统:MySQL建模到事务并发避坑全解析
简介这是一份数据库课程设计报告主题为外卖点餐管理系统适合计算机相关专业学生完成数据库综合设计作业时参考。报告从项目背景切入完整梳理了管理员、店铺、客服、送货员、订单及配送等核心业务需求并配有总数据流图与订餐管理、员工管理、订单派送等分数据流图。数据字典覆盖管理员、客服、送货员、店铺、订单及物流实体字段说明详尽。系统设计部分采用MySQL加Python按表示层、业务逻辑层和数据访问层三层架构展开介绍了用户注册登录、店铺管理、客服管理、送货员管理、订单管理和配送管理等模块。资源为单个docx文档压缩包大小2.92MB便于直接查看与编辑已有165人学习。报告结构规范、图表齐全可作为课程设计文档的格式参考也可用于快速梳理外卖类管理系统的数据库建模思路与功能模块划分。1. 外卖点餐管理系统数据库课程设计到底在写什么、给谁看拿到题目「数据库课程设计报告外卖点餐管理系统.docx」时先别急着找代码。数据库课程设计报告和普通项目代码是两码事——老师拿到这份 Word 文档重点看的是你如何把真实的外卖点餐业务拆成实体、属性和关系能不能用合理的 SQL 建出表来并完成增删改查并发情况下订单会不会出错。这套东西跑通设计也就立住了。适合正在做课设、需要快速照着实现一遍的学生也适合想知道「外卖订单表到底怎么设计才不出丑」的开发者。报告只是汇报载体背后的 MySQL 建模和 SQL 才是你要真正交付的。2. 从外卖点餐倒推表设计ER 图、三范式与六张核心表一份数据库课程设计报告的评分重心通常落在需求分析与概念设计。外卖点餐系统如果一上来就写代码ER 图画得稀里糊涂后面所有表都可能返工。我一般会先花半天把业务流程走一遍再动手建模。2.1 点餐流程里藏着哪些实体与属性先别看「管理系统」四个字就无脑堆功能。外卖点餐的业务闭环是用户注册登录、浏览商家、选择菜品、提交订单、支付、商家接单、配送、完成。整个过程里反复出现的是用户、商家、菜品、订单、配送员五个业务主体订单和菜品之间还有「某个订单买了哪几份菜」的关联这就是订单明细。ER 图阶段建议把实体和属性列成一张表防止画图时丢字段。下面这张表可以直接抄进报告当作「实体-属性说明」。实体关键属性关系说明用户 usersuser_id, username, password_hash, phone, address, balance, reg_time一个用户可下多个订单商家 shopsshop_id, shop_name, address, phone, rating一个商家可上架多个菜品菜品 dishesdish_id, shop_id, dish_name, price, stock, status菜品属于某个商家订单 ordersorder_id, user_id, shop_id, delivery_id, order_time, total_amount, status, receiver_addr, remark一个订单对应一个用户、一个商家、一位配送员订单明细 order_detailsdetail_id, order_id, dish_id, dish_name, price, quantity一个订单可包含多条明细配送员 delivery_mendelivery_id, delivery_name, phone一个配送员可配送多个订单字段命名和类型在 ER 图阶段就要想好否则后面建表时反复改。常见做法是字符串 id 一律用 INT AUTO_INCREMENT金额用 DECIMAL(10,2) 而不是 FLOAT 或 DOUBLE避免浮点误差手机号用 VARCHAR(20) 而不是数值类型因为前导零和加号都存在密码不存明文password_hash 用 CHAR(64) 或 VARCHAR(64) 存摘要。这些都是报告里数据字典的素材早定早省事。购物车我不建议放进核心 ER 图。用户选菜到下单之间是临时状态不需要持久化如果老师明确要求可以加一个 cart 表但不要让它在订单链路里承担核心角色。订单明细才是用户和菜品之间多对多关系的正式纽带——没有它用户和菜品会形成无法直接建模的多对多违反关系模型的基本原则。2.2 把 ER 图转成 3NF 关系模式六张核心表关系模式转换规则很固定每个实体一张表1:N 关系把 1 方主键放进 N 方作外键多对多拆关联表。把上面的 ER 图落下来核心关系模式如下下划线表示主键users(user_id, username, password_hash, phone, address, balance, reg_time)shops(shop_id, shop_name, address, phone, rating)dishes(dish_id, shop_id, dish_name, price, stock, status)delivery_men(delivery_id, delivery_name, phone)orders(order_id, user_id, shop_id, delivery_id, order_time, total_amount, status, pay_method, receiver_addr, remark)order_details(detail_id, order_id, dish_id, dish_name, price, quantity, subtotal)从 ER 图到关系模式的转换我习惯按四步走先把每个实体拆成一张表再选主键然后找外键最后检查多对多。外卖里用户和菜品天然是多对多一个用户买很多菜一道菜被很多用户买。直接建 user_dish 关联表是不对的因为下单这个动作本身有订单时间、金额、状态应该拆成订单主表和订单明细表由明细表承担多对多关联。对照三范式检查一遍第一范式要求字段不可再分上面每个字段都是原子值第二范式要求非主键字段完全依赖主键order_details 里如果还有「订单时间」它只依赖 order_id 而不依赖 detail_id就会出现部分依赖必须拆到 orders 表第三范式要求消除传递依赖例如用户地址如果通过 users 表间接决定订单的配送地址就存在传递依赖所以我们把收货地址 receiver_addr 直接冗余进订单表作为下单时的快照这与纯三范式有冲突但业务上必须保留。另一个容易被答辩老师盯住的是 status 字段。订单状态在报告里不要只用 TINYINT 和一个隐藏注释最好在数据字典里写明枚举含义0 待支付、1 已支付、2 制作中、3 配送中、4 已完成、5 已取消。配送员的配送状态也可以类似设计但不要和订单状态混成一张状态表状态机分散在各业务表里更直观。表引擎统一选 InnoDB。很多老教程还写着 MyISAM但 MyISAM 不支持事务和外键后面下单扣库存的存储过程一旦落到 MyISAM 上事务就静默失效。报告里最好也把「统一选用 InnoDB事务是订单一致性前提」写进建表说明这让评卷人能直接看到你的选型依据。2.3 关系模式必须回答的 4 个设计问题订单主表和明细表为什么要分开。一个订单包含多道菜如果合成一张表一个订单会占多行订单状态、收货地址、总金额会被重复存储。更麻烦的是用户改一次订单状态要找所有明细行去更新容易更新一半出 bug。拆开后 orders 一行一个订单order_details 多行一个订单职责清晰查询也自然。total_amount 要不要存。我的答案是存。虽然可以从明细行单价乘数量加总但每次查订单列表都要 join 明细数据量上来后代价很高更重要的是订单金额一旦生成就不该因为菜品改价而漂移落库成为不可变历史才符合业务。报告里可以补一句「total_amount 由下单事务计算写入应用层不手工改」。菜品改名或改价后历史订单会不会被污染。会。如果不做任何处理明细里只存 dish_id查询时实时 join 菜品表取名称和价格一旦菜品改价昨天的订单显示今天的价格对账和用户投诉都麻烦。因此在 order_details 里冗余 dish_name 和 price 两个快照字段历史查询一律用明细里的值。这是反范式但是正确答案。用户改地址后正在配送的订单怎么办。用户地址如果只在 users 表里维护改地址会把所有历史订单的收货地址一起改掉配送员全跑错。所以 orders 表里要有 receiver_addr 快照下单那一刻把地址写进订单之后用户改地址只影响下一次下单。这类「快照冗余」是外卖系统里最实用的设计技巧写在报告里会显得你有真实业务意识。3. 建库建表与增删改查用 MySQL 8.4 LTS 跑通最小闭环关系模式定稿后进入物理实现阶段。课程设计常用的数据库是 MySQL当前环境下用 8.4.11 LTS 这类长支持版本比 5.7 更合适安装方式和老版本差别不大。下面都以 Windows 本地环境为例Linux 把服务安装命令换成 systemctl 即可。3.1 下载、解压及配置MySQL 8.4.11 LTS 落地本地先去官网镜像下载 mysql-8.4.11-lts-windows-x64.zip解压到 D:\mysql-8.4.11。注意解压目录不要带中文和空格否则后面前后端联调容易出怪问题。在解压目录下新建 my.ini内容如下[mysqld] basedirD:/mysql-8.4.11 datadirD:/mysql-8.4.11/data port3306 character-set-serverutf8mb4这个文件是 MySQL 启动时的主配置。basedir 指向解压目录datadir 指向数据文件目录port 保持 3306 即可character-set-server 必须写成 utf8mb4否则中文和 emoji 写入时会出现乱码或长度报错。保存后打开「管理员权限的命令行」依次执行mysqld --initialize --console mysqld --install MySQL84 net start MySQL84说明一下参数--initialize是初始化数据目录会自动生成 data 文件夹和一套初始系统表--console会把临时 root 密码打印到终端务必保存初始化只要执行一次如果第二次执行会提示 data 目录已存在。mysqld --install MySQL84是把 MySQL 注册成 Windows 服务服务名自己取net start 用来启动它。启动后用临时密码登录改掉初始密码ALTER USER rootlocalhost IDENTIFIED BY Course2024; FLUSH PRIVILEGES;注意 MySQL 8 默认认证插件是 caching_sha2_password连接工具和 JDBC 驱动都要用 8.x 版本旧版 5.x 驱动会握手失败。这个问题在避坑章节还会展开。3.2 建库建表 SQL订单主表、明细表与索引课程设计报告附录里通常要放完整建表脚本下面是一套可直接运行的建库建表 SQL覆盖第二章的关系模式。DROP DATABASE IF EXISTS delivery_db; CREATE DATABASE delivery_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; USE delivery_db; CREATE TABLE users ( user_id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(32) NOT NULL, password_hash VARCHAR(64) NOT NULL, phone VARCHAR(20), address VARCHAR(200), balance DECIMAL(10,2) DEFAULT 0.00, reg_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_username (username) ) ENGINEInnoDB; CREATE TABLE shops ( shop_id INT AUTO_INCREMENT PRIMARY KEY, shop_name VARCHAR(64) NOT NULL, address VARCHAR(200), phone VARCHAR(20), rating DECIMAL(2,1) DEFAULT 5.0 ) ENGINEInnoDB; CREATE TABLE dishes ( dish_id INT AUTO_INCREMENT PRIMARY KEY, shop_id INT NOT NULL, dish_name VARCHAR(64) NOT NULL, price DECIMAL(10,2) NOT NULL, stock INT NOT NULL DEFAULT 0, status TINYINT DEFAULT 1, CONSTRAINT fk_dishes_shop FOREIGN KEY (shop_id) REFERENCES shops(shop_id) ) ENGINEInnoDB; CREATE TABLE delivery_men ( delivery_id INT AUTO_INCREMENT PRIMARY KEY, delivery_name VARCHAR(32) NOT NULL, phone VARCHAR(20) ) ENGINEInnoDB; CREATE TABLE orders ( order_id INT AUTO_INCREMENT PRIMARY KEY, user_id INT NOT NULL, shop_id INT NOT NULL, delivery_id INT, order_time DATETIME DEFAULT CURRENT_TIMESTAMP, total_amount DECIMAL(10,2) NOT NULL, status TINYINT DEFAULT 0 COMMENT 0待支付,1已支付,2制作中,3配送中,4已完成,5已取消, pay_method VARCHAR(16), receiver_addr VARCHAR(200), remark VARCHAR(255), CONSTRAINT fk_orders_user FOREIGN KEY (user_id) REFERENCES users(user_id), CONSTRAINT fk_orders_shop FOREIGN KEY (shop_id) REFERENCES shops(shop_id), CONSTRAINT fk_orders_delivery FOREIGN KEY (delivery_id) REFERENCES delivery_men(delivery_id) ) ENGINEInnoDB; CREATE TABLE order_details ( detail_id INT AUTO_INCREMENT PRIMARY KEY, order_id INT NOT NULL, dish_id INT NOT NULL, dish_name VARCHAR(64) NOT NULL, price DECIMAL(10,2) NOT NULL, quantity INT NOT NULL DEFAULT 1, subtotal DECIMAL(10,2) GENERATED ALWAYS AS (price * quantity) STORED, CONSTRAINT fk_details_order FOREIGN KEY (order_id) REFERENCES orders(order_id) ON DELETE CASCADE, CONSTRAINT fk_details_dish FOREIGN KEY (dish_id) REFERENCES dishes(dish_id) ) ENGINEInnoDB;建表参数说明DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci统一数据库默认字符集避免中文乱码ENGINEInnoDB启用事务和行级锁这是后面下单扣库存的基础MyISAM 虽然查询快但不能保证并发一致性不建议用AUTO_INCREMENT作为自增主键适合外卖这类写入频繁的订单业务FOREIGN KEY保证引用完整性删除订单时通过ON DELETE CASCADE自动清理明细但其他外键都刻意没加级联原因在避坑章节说。订单明细里的subtotal我用的是生成列GENERATED ALWAYS AS (price * quantity) STOREDMySQL 会在写入时自动算出小计应用层少一次乘法也少一类「金额对不上」的 bug。如果用的 MySQL 版本低于 5.7生成列可能不支持课设环境可以用subtotal DECIMAL(10,2) NOT NULL DEFAULT 0替代由代码计算。接下来补两个查询最常用的索引。外键关联字段 MySQL 会自动建索引但 orders 表按用户查最近订单、order_details 按订单查明细必须手动加组合索引才能避免全表扫描ALTER TABLE orders ADD INDEX idx_user_time (user_id, order_time); ALTER TABLE order_details ADD INDEX idx_order_dish (order_id, dish_id);第一个索引服务于「用户订单列表页」查询条件是 user_id排序是 order_time第二个索引服务于「订单详情页」先按 order_id 找到明细再按 dish_id 去补菜品信息。索引不是越多越好这两个基本覆盖课设全部高频路径。3.3 基础增删改查四个必考用例数据库课程设计答辩时老师常常现场点人写一句 SQL。增删改查是底线下面这组用例基本覆盖外卖系统核心功能可以直接对照报告里的功能模块。-- 新增用户 INSERT INTO users (username, password_hash, phone, address) VALUES (alice, SHA2(123456, 256), 13800000000, 中关村大街1号); -- 给 1 号商家上架一道菜 INSERT INTO dishes (shop_id, dish_name, price, stock) VALUES (1, 宫保鸡丁, 28.00, 50); -- 查询 1 号商家的在售菜品按价格升序 SELECT dish_id, dish_name, price, stock FROM dishes WHERE shop_id 1 AND status 1 ORDER BY price ASC; -- 修改菜品价格 UPDATE dishes SET price 30.00 WHERE dish_id 1; -- 下架并删除一道菜 DELETE FROM dishes WHERE dish_id 5 AND status 0;参数说明SHA2(123456, 256)比老教程里的 MD5 更合适虽然课程设计不要求安全强度但报告写出来至少不像十年前的做法WHERE status 1是「在售」状态用状态筛选而不是直接删行是逻辑删除的设计先兆更新和删除都带主键条件避免误改全表。再来一条跨多表查询这是查询能力的高频考点。查某个用户最近的三个订单及明细SELECT o.order_id, o.order_time, o.total_amount, d.dish_name, d.price, d.quantity FROM orders o JOIN order_details d ON o.order_id d.order_id WHERE o.user_id 1 ORDER BY o.order_time DESC, o.order_id DESC LIMIT 3;这条 SQL 把 orders 和 order_details 两张表通过外键关联起来LIMIT 3 只取最近三个订单。注意同一个订单有多条明细时结果集会返回多行如果要在前端展示成「三个订单卡片」通常还是先查订单主表再按 order_id 一次性查明细避免在应用层做行转对象。3.4 用事务把「下单扣库存」做成一个安全流程点餐系统里最见功力的不是单表增删改查而是「下单」这个跨表事务。一个完整订单要同时更新菜品库存、插入订单主表、插入订单明细任何一步失败都不应该留下脏数据。下面用存储过程把三条操作包进一个事务里DELIMITER $$ CREATE PROCEDURE sp_create_order( IN p_user_id INT, IN p_shop_id INT, IN p_delivery_id INT, IN p_dish_id INT, IN p_quantity INT, IN p_receiver_addr VARCHAR(200) ) BEGIN DECLARE dish_price DECIMAL(10,2); DECLARE total DECIMAL(10,2); START TRANSACTION; -- 条件更新库存充足才扣减避免先查后改造成超卖 UPDATE dishes SET stock stock - p_quantity WHERE dish_id p_dish_id AND stock p_quantity; IF ROW_COUNT() 0 THEN ROLLBACK; SIGNAL SQLSTATE 45000 SET MESSAGE_TEXT 库存不足; END IF; SELECT price INTO dish_price FROM dishes WHERE dish_id p_dish_id; SET total dish_price * p_quantity; INSERT INTO orders (user_id, shop_id, delivery_id, total_amount, status, receiver_addr) VALUES (p_user_id, p_shop_id, p_delivery_id, total, 1, p_receiver_addr); INSERT INTO order_details (order_id, dish_id, dish_name, price, quantity) VALUES (LAST_INSERT_ID(), p_dish_id, (SELECT dish_name FROM dishes WHERE dish_id p_dish_id), dish_price, p_quantity); COMMIT; END$$ DELIMITER ;这个存储过程能完整跑通靠的是几个关键细节。UPDATE ... WHERE stock p_quantity是在一条语句里同时完成「检查库存」和「扣减库存」数据库行锁会锁住该菜品行直到事务提交如果库存不足影响行数为 0直接回滚并抛异常。注意IF ROW_COUNT() 0必须紧跟 UPDATE中间不能夹 SELECT否则 ROW_COUNT() 会被覆盖。SELECT price INTO dish_price在 UPDATE 之后执行菜品行已经被锁住价格不会中途变化。LAST_INSERT_ID()取的是当前会话刚刚插入的 orders 主键用它作为明细外键才能把主子表挂上。调用方式很简单把参数按声明顺序传入即可CALL sp_create_order(1, 1, 1, 101, 2, 中关村大街1号); SELECT * FROM orders ORDER BY order_id DESC; SELECT * FROM order_details ORDER BY detail_id DESC;执行后能看到 orders 新增一行、order_details 新增两行数量为 2对应菜品库存减少 2。这里的「条件更新 行锁」就是数据库并发锁的初步应用也是后面避坑章节里并发超卖问题的正解之一。4. 连接池、视图与触发器把设计从「能用」变成「能答辩」许多课设做到「能跑」就停了但评分表里通常有系统实现和优化一项。这章把三个性价比最高的优化点讲清楚连接池、视图加组合索引、触发器。三者都能在报告里形成「优化前后」的对比比堆功能好写。4.1 数据库连接池课程设计最容易被挑刺的地方课程设计代码最常见的低级错误是每个方法里都直接DriverManager.getConnection()用完还不关。数据库连接的建立要经过 TCP 握手、认证、分配会话一次可能花几十毫秒高并发时大量连接堆积数据库直接卡死。正确做法是用数据库连接池让少量连接循环复用。以 Spring Boot 项目为例HikariCP 是默认内置连接池在 application.properties 里配一段即可spring.datasource.urljdbc:mysql://localhost:3306/delivery_db?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue spring.datasource.usernameroot spring.datasource.passwordCourse2024 spring.datasource.driver-class-namecom.mysql.cj.jdbc.Driver spring.datasource.hikari.maximum-pool-size10 spring.datasource.hikari.minimum-idle2 spring.datasource.hikari.connection-timeout30000参数说明maximum-pool-size10不是越大越好课程设计的并发量连 10 都用不满调成 50 只会让数据库多开一堆空闲连接minimum-idle2保持最少两个空闲连接避免请求一来才现场建连connection-timeout30000是获取连接的超时超过 30 秒直接报错避免前端无限转圈。URL 里的allowPublicKeyRetrievaltrue是 MySQL 8 的 caching_sha2_password 认证需要的不加会报Public Key Retrieval is not allowed。连接池配好之后真正要命的是代码里有没有正确归还连接。JDBC 方式务必把关闭动作放进 finally 或 try-with-resources否则连接池会被借光表现为接口卡死、活动连接数打满。验证方法也简单Spring Boot 启动日志里能看到 HikariPool 启动数据库端Threads_connected保持个位数而不是接口一访问就涨到几十。4.2 用视图和组合索引把「查询数据库」做成亮点外卖后台经常要查「订单汇总列表」每次把 orders、users、shops 三张表 join 一遍SQL 又长又容易漏条件。这时可以创建视图把常用关联固定下来CREATE VIEW v_order_summary AS SELECT o.order_id, u.username, s.shop_name, o.order_time, o.total_amount, o.status FROM orders o JOIN users u ON o.user_id u.user_id JOIN shops s ON o.shop_id s.shop_id WHERE o.status ! 5;视图在 MySQL 里相当于一个保存好的查询逻辑应用层查SELECT * FROM v_order_summary WHERE order_id 100就行不用关心底层 join。注意视图不是物理表它不会提升查询性能真正提速要靠索引。在课程设计报告里建议把视图对应一个页面比如 v_order_summary 对应后台「订单管理页」视图名保持 v_ 前缀与表名区分老师看文档时能一一对上。前面 3.2 已经加了两个组合索引如何证明它们有效用 EXPLAINEXPLAIN SELECT * FROM orders WHERE user_id 1 ORDER BY order_time DESC;看输出里的 type 和 key 两列。type 是 ALL 时说明全表扫描表里有几千行测试数据时看不出来课程设计为了演示效果可以造两万行数据再对比type 为 ref 或 range 且 key 显示idx_user_time说明组合索引被用上了。把这个 EXPLAIN 结果截图放进报告「性能优化」一节比写十页文字有说服力。4.3 触发器记录订单状态变更一个能讲清楚的小设计订单状态从待支付到已完成会反复变化如果不记录操作日志一旦数据不对就只能面对黑匣子。可以用触发器在状态变化时自动写日志表先建一张简单的日志表CREATE TABLE order_status_log ( log_id INT AUTO_INCREMENT PRIMARY KEY, order_id INT NOT NULL, old_status TINYINT, new_status TINYINT, changed_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB;然后为 orders 表建一个 AFTER UPDATE 触发器DELIMITER $$ CREATE TRIGGER trg_order_status_update AFTER UPDATE ON orders FOR EACH ROW BEGIN IF OLD.status NEW.status THEN INSERT INTO order_status_log (order_id, old_status, new_status) VALUES (NEW.order_id, OLD.status, NEW.status); END IF; END$$ DELIMITER ;触发器逻辑一目了然UPDATE 时只要状态值变化就往日志表插一条记录。OLD 是更新前的一行NEW 是更新后的一行。之后无论前端从哪里改状态日志都会自动留下。这里要提醒一句触发器适合做「轻量审计」不要在里面写复杂业务。它默认在同一个事务里执行如果触发器报错主操作也会回滚别人看代码时触发器是隐藏逻辑很不容易排查。课程设计里放一个触发器当亮点就够了别每个表都挂一套否则答辩时解释不清反而成为减分项。5. 数据库课程设计避坑并发、死锁、外键和连接池前面几章的代码都是理想路径真实运行中翻车最多的是并发和外键设计。下面五条坑来自数据库课程设计里最常见的失败现场每条按「现象、原因、解决」写可以直接变成报告里的「问题与调试分析」章节。5.1 并发下单导致超卖库存变成负数现象两个浏览器同时点「下单」数据库里菜品库存从 1 变成 -1订单却成功创建了。原因代码先SELECT stock FROM dishes判断库存大于 0再UPDATE dishes SET stock stock - 1。两个请求同时读到库存 1都判定可以下单各自扣减后就出现 -1。这是典型的「先查后改」竞态也是数据库并发锁知识点最喜欢考的案例。解决3.4 的存储过程已经给出正解——用条件更新UPDATE dishes SET stock stock - p_quantity WHERE dish_id ... AND stock p_quantity库存不够时影响行数为 0事务回滚。如果不想用存储过程也可以用悲观锁先锁行再改START TRANSACTION; SELECT stock FROM dishes WHERE dish_id 101 FOR UPDATE; UPDATE dishes SET stock stock - 1 WHERE dish_id 101; COMMIT;FOR UPDATE会锁住菜品行直到事务结束第二个事务只能排队。把这两种写法都写进报告说明你理解并发下的一致性答辩时很容易过。5.2 MySQL 死锁加锁顺序不一致导致的循环等待现象并发测试时数据库偶尔报Deadlock found when trying to get lock; try restarting transaction接口 500。原因一个订单同时包含多道菜事务 A 先锁菜品 1 再锁菜品 2事务 B 先锁菜品 2 再锁菜品 1。A 拿着 1 锁等 2B 拿着 2 锁等 1形成循环等待InnoDB 检测到后回滚其中一个事务。解决所有事务访问多行必须按固定顺序加锁比如按 dish_id 升序。实际下单时先把购物车里的菜品 id 排序再调存储过程这样加锁顺序全局一致。死锁不可能完全避免应用层还要捕获死锁错误并重试。Python 示例import pymysql from pymysql.constants.ER import LOCK_DEADLOCK for attempt in range(3): try: with conn.cursor() as cur: cur.execute(CALL sp_create_order(1, 1, 1, 101, 2, 中关村大街1号)) conn.commit() break except pymysql.err.OperationalError as e: if e.args[0] ! LOCK_DEADLOCK or attempt 2: raise conn.rollback()死锁不是数据库坏了恰恰是它保护一致性的手段。把死锁重试写进课程设计报告是很大的加分项因为大多数学生连死锁日志都看不懂。5.3 外键级联删除删了商家历史订单跟着没了现象管理员删除一个测试商家数据库报外键约束错误删不掉部分同学改成ON DELETE CASCADE后商家和它的菜品、订单、明细被连带删除。原因orders 表和 dishes 表都引用了商家主键默认外键会阻止删除被引用的行如果给每个外键都级联删除范围会蔓延到订单明细历史订单数据全部蒸发。解决外键规则按从属关系设置。order_details 完全从属于 orders可以ON DELETE CASCADE订单删了明细跟着删orders 和 dishes 与 shops 之间是业务引用关系不能级联删除。正确的业务数据删除姿势是逻辑删除给 status 置 5已取消或给表加deleted字段不到万不得已不做物理删除。报告里写清楚「逻辑删除 受限外键」处理订单历史数据时就不会翻车。5.4 连接池耗尽前端卡死Threads_connected 打满现象并发压测跑一会儿前端接口全部卡住SHOW STATUS LIKE Threads_connected接近 200数据库 CPU 飙高。原因应用代码拿连接后没有归还连接池被借空。最常见的是查询出现异常直接 return 忘记关连接或者个别方法自己new Connection走的是连接池之外的裸连接。解决用 try-with-resources 或 finally 保证连接关闭先查连接状态SHOW STATUS LIKE Threads_connected; SHOW PROCESSLIST;Threads_connected持续高位说明有连接泄漏SHOW PROCESSLIST看哪些连接长期处于 Sleep 又不归还。把它们对应的代码段找出来补上关闭逻辑Threads_connected 会立刻掉下来。这条经验在演示性能测试时特别实用别等到答辩现场才暴露。5.5 MySQL 8 与旧驱动握手失败Public Key Retrieval 报错现象项目用 mysql-connector-java 5.1.x 连接本机 MySQL 8.4启动报Public Key Retrieval is not allowed或者Authentication plugin caching_sha2_password cannot be loaded。原因MySQL 8 默认认证插件是 caching_sha2_password老驱动只认识 mysql_native_password双方握手失败。解决升级依赖到 mysql-connector-j 8.x并在 JDBC URL 加上allowPublicKeyRetrievaltrueuseSSLfalse。如果课程设计必须用老驱动可以把 root 用户临时切回 mysql_native_password但不建议在生产环境这么做。这属于最容易排查也最容易忽略的环境坑写进报告的「系统测试环境」一节能少扣印象分。6. 答辩前两小时用「数据字典 测试记录」验收整个系统课程设计报告最后别急着交先用一组可重复的测试数据把核心链路跑一遍再对着验收清单检查。6.1 用一条完整下单链路做回归测试准备一个新用户和一道菜执行一次下单再查询订单和明细核对库存变化CALL sp_create_order(1, 1, 1, 101, 1, 中关村大街1号); SELECT u.username, o.order_id, o.total_amount, d.dish_name, d.price, d.quantity FROM orders o JOIN order_details d ON o.order_id d.order_id JOIN users u ON o.user_id u.user_id WHERE o.user_id 1;如果能查到一行用户信息、一行订单、一行菜品明细且 total_amount 等于 price × quantity这条链路就通了。再把库存减 1 的断言加进测试记录截图放进报告。6.2 用 EXPLAIN 和状态命令确认没有明显慢查询对第三章的组合索引执行EXPLAIN确认 type 不是 ALL执行SHOW STATUS LIKE Threads_connected确认连接数没被打满。这些输出直接贴进「系统测试与优化」章节比声称「系统性能良好」可信得多。最后想提醒一个我自己的教训。我当年做课设时表建得很完整但漏了操作日志订单状态反复被前端请求调试查问题时全靠猜。后来给状态变更加了一行记录才把问题从黑匣子变成可回查的数据。建议你也在报告里增加「状态日志」这个表的设计说明答辩时会多一个可聊的细节。希望帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑