SpringBoot+MySQL外卖系统实战:从零搭建苍穹外卖后台(含完整数据库设计)
SpringBootMySQL外卖系统实战高可用数据库设计与避坑指南1. 企业级外卖系统的数据库架构设计外卖平台的数据库设计直接决定了系统的扩展性和稳定性。一个优秀的数据库架构需要兼顾业务复杂性和性能需求同时为未来功能迭代预留空间。1.1 核心业务表设计原则**地址簿表(Address Book)**的设计体现了典型的地理位置存储方案CREATE TABLE address_book ( id bigint NOT NULL AUTO_INCREMENT COMMENT 主键, user_id bigint NOT NULL COMMENT 用户id, consignee varchar(50) DEFAULT NULL COMMENT 收货人, phone varchar(11) NOT NULL COMMENT 手机号, province_code varchar(12) DEFAULT NULL COMMENT 省级区划编号, province_name varchar(32) DEFAULT NULL COMMENT 省级名称, city_code varchar(12) DEFAULT NULL COMMENT 市级区划编号, district_code varchar(12) DEFAULT NULL COMMENT 区级区划编号, detail varchar(200) DEFAULT NULL COMMENT 详细地址, is_default tinyint(1) DEFAULT 0 COMMENT 默认地址标识 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;关键设计要点采用三级行政区划编码而非直接存储名称便于后续与地理信息系统对接。手机号字段设置为NOT NULL保证配送联系可靠性。**菜品分类表(Category)**采用类型字段区分不同分类体系CREATE TABLE category ( id bigint NOT NULL AUTO_INCREMENT, type int DEFAULT NULL COMMENT 1 菜品分类 2 套餐分类, name varchar(32) NOT NULL, sort int DEFAULT 0 COMMENT 展示顺序, status int DEFAULT NULL COMMENT 0:禁用1:启用, create_time datetime DEFAULT NULL, update_time datetime DEFAULT NULL ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;1.2 订单系统的分表策略订单系统采用主表明细表的设计模式表名字段数核心字段索引设计orders21订单状态、支付方式、配送状态联合索引(user_id, order_time)order_detail8菜品ID、口味要求、数量订单ID外键索引这种设计将订单基本信息和商品明细分离避免了大字段导致的性能问题同时方便进行分库分表扩展。2. SpringBoot与MySQL的深度整合技巧2.1 JDBC连接池优化配置在application.yml中配置Druid连接池参数spring: datasource: type: com.alibaba.druid.pool.DruidDataSource druid: initial-size: 5 min-idle: 5 max-active: 20 test-on-borrow: true validation-query: SELECT 1 filters: stat,wall生产环境建议根据服务器CPU核心数设置max-active通常为(核心数*2)有效磁盘数2.2 事务管理实战方案使用声明式事务管理外卖订单创建流程Service RequiredArgsConstructor public class OrderServiceImpl implements OrderService { private final OrderMapper orderMapper; private final OrderDetailMapper detailMapper; Transactional(rollbackFor Exception.class) public void createOrder(OrderDTO orderDTO) { // 1. 插入订单主表 Orders order new Orders(); BeanUtils.copyProperties(orderDTO, order); orderMapper.insert(order); // 2. 批量插入订单明细 ListOrderDetail details orderDTO.getDetails().stream() .map(d - { OrderDetail detail new OrderDetail(); BeanUtils.copyProperties(d, detail); detail.setOrderId(order.getId()); return detail; }).collect(Collectors.toList()); detailMapper.insertBatch(details); } }3. 开发环境疑难问题解决方案3.1 Lombok与JDK版本兼容问题当遇到编译错误java: 找不到符号 符号: 方法 getUsername()解决方案分三步检查pom.xml中的Lombok版本properties lombok.version1.18.30/lombok.version /properties确认IDE安装了对应版本的Lombok插件在IDEA中开启注解处理Settings → Build → Compiler → Annotation Processors ✔ Enable annotation processing3.2 多模块项目的依赖管理采用Maven的dependencyManagement统一管理依赖版本dependencyManagement dependencies dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version${mybatis.spring}/version /dependency !-- 其他依赖统一管理 -- /dependencies /dependencyManagement4. 生产环境部署与性能调优4.1 Nginx反向代理配置外卖系统典型配置upstream backend { server 192.168.1.101:8080 weight5; server 192.168.1.102:8080 weight3; keepalive 32; } server { location /api/ { proxy_pass http://backend/admin/; proxy_http_version 1.1; proxy_set_header Connection ; } }关键参数说明keepalive维持的长连接数量proxy_http_version 1.1启用HTTP1.1持久连接weight服务器权重分配4.2 MySQL性能优化建议针对外卖系统的特殊优化-- 订单表添加覆盖索引 ALTER TABLE orders ADD INDEX idx_user_status (user_id, status); -- 优化菜品查询 ALTER TABLE dish ADD INDEX idx_category_status (category_id, status);监控慢查询日志配置[mysqld] slow_query_log 1 slow_query_log_file /var/log/mysql/mysql-slow.log long_query_time 1 log_queries_not_using_indexes 15. 项目规范与最佳实践5.1 统一响应格式设计采用泛型封装响应结果Data public class ResultT { private Integer code; private String msg; private T data; public static T ResultT success(T data) { ResultT result new Result(); result.code 200; result.data data; return result; } }5.2 异常处理机制自定义异常体系结构BaseException ├── BusinessException ├── AuthException └── DatabaseException全局异常处理器示例RestControllerAdvice public class GlobalExceptionHandler { ExceptionHandler(BusinessException.class) public ResultVoid handleBusinessEx(BusinessException ex) { return Result.error(ex.getCode(), ex.getMessage()); } }5.3 常量集中管理创建常量接口避免魔法值public interface OrderConstants { int STATUS_UNPAID 1; int STATUS_PAID 2; int STATUS_CANCELED 6; String CACHE_PREFIX order:; }实际项目中我们会将这些常量与Redis缓存键设计相结合例如订单状态变更时自动更新缓存public void updateOrderStatus(Long orderId, int newStatus) { // 更新数据库 orderMapper.updateStatus(orderId, newStatus); // 清除缓存 redisTemplate.delete(OrderConstants.CACHE_PREFIX orderId); }