☰
SpringBoot+Vue前后端分离在线教育平台管理系统实战
2026/10/22 14:53:13 网站建设 项目流程

做在线教育平台管理系统这个项目,是我把SpringBoot+Vue这套前后端分离架构完整跑通的一次实战。整套代码覆盖了在线教育后台最核心的几条业务链:课程管理、讲师信息维护、学员学习记录、后台账号权限、订单报名数据。技术栈很固定,SpringBoot做后端服务,MyBatis负责持久层SQL,MySQL存业务数据,前端用Vue写管理界面。这篇文章不打算写那种面面俱到的官方文档,而是从实际落地角度,把架构拆分、数据库设计、核心接口实现、踩坑记录这几块讲透。适合正在做毕业设计、准备面试项目、或者想从零把一套在线教育后台跑起来的开发者参考。

1. 项目到底解决了什么问题

1.1 在线教育系统的核心需求拆解

很多人第一次拿到在线教育平台这种需求,第一反应是“不就是课程列表加个播放器吗”。真做起来就发现,后台管理系统的复杂度远高于前台展示页。在线教育平台的管理后台,至少需要覆盖这几个角色和场景:

管理员要能够维护课程上下架、审核讲师入驻、查看平台运营数据。讲师需要上传课程章节、管理题库、布置作业、查看自己负责班级的学习情况。学员则需要查看已购课程、记录学习进度、完成章节测试。如果再往复杂了说,还有优惠券、直播课、班级分组、多校区权限,但最核心的底座永远是课程和用户两条线。

我做的这个系统,核心定位是后台管理端加简单的学员端查询能力。把“课程维护—讲师管理—学员管理—学习记录”这条主链路打通。为什么要先做后台而不是前台?因为在线教育平台的真实瓶颈几乎都出在内容维护和权限控制上。前台页面再花哨,后台如果连课程上下架、讲师角色权限都理不清楚,业务根本跑不起来。

需求梳理阶段,我习惯把功能拆成“必须要有”和“有了更好”两类。必须有的:用户登录注册、角色权限、课程分类、课程与章节管理、讲师资料维护、学员学习进度记录。可以后补的:直播互动、课程评论、积分系统、数据报表大屏。这套系统在初版把“必须有”的做扎实,后续扩展时再往上面加模块,结构不会乱。

1.2 为什么选SpringBoot+Vue+MyBatis这套组合

说句实在话,在线教育管理系统这种CRUD密集型业务,用很多技术栈都能做。但选SpringBoot+Vue+MyBatis+MySQL,不是跟风,而是这套组合在“开发效率、维护成本、上手难度”三个维度上非常均衡。

SpringBoot解决的是后端基础设施的繁琐问题。以前用Spring MVC搭项目,要写一堆XML配置,现在SpringBoot通过自动配置和Starter机制,把大部分常见场景都预设好了。你只需要引入依赖,写少量配置,就能把Web服务跑起来。对于以业务逻辑为主的系统,这种“开箱即用”的体验非常舒服。

Vue在前端的优势是组件化开发。后台管理界面有大量重复的表格、表单、弹窗场景,用组件封装之后,写一个新页面就是组合几个组件的事。Vue的响应式数据绑定也让表单交互和列表筛选省了很多精力,不需要手动操作DOM。

MyBatis经常被拿来和JPA对比。我的个人观点是,在线教育系统里面高级查询和统计SQL特别多,比如“查询某讲师名下所有课程的学员完成率”,用MyBatis写SQL可以精确控制语句,排查问题也直观。JPA虽然写简单CRUD很爽,但一旦遇到复杂动态SQL,调试成本会明显上升。MyBatis的灵活性和可控性,在这个场景里更值得选。

MySQL就更不用多说了,成熟的社区生态、丰富的文档、对事务的支持,足够支撑在线教育平台这个体量的数据。真正要关心的是表结构设计和索引优化,这个放到后面数据库部分详细讲。

2. 系统整体架构与模块设计

2.1 前后端分离的整体思路

这套系统的架构不复杂,就是标准的前后端分离:Vue前端通过HTTP请求访问SpringBoot后端的RESTful接口,后端操作MySQL数据库。前后端分别开发、分别部署,前端打包成静态文件丢到Nginx,后端打成Jar包运行在服务器上。

项目根目录下一般会分成两个部分:

  • frontend目录:Vue项目,包含管理后台的页面、路由、状态管理、API请求封装。
  • backend目录:SpringBoot项目,包含Controller、Service、Mapper、实体类、配置类。

这种结构最大的好处是团队协作时可以并行开发。前端根据接口文档定义Mock数据,后端把接口实现好之后联调即可。即便一个人开发,分开写也能让思维更清晰,不会在改前端样式的时候被后端代码干扰。

后端内部我做了一层严格的分层:Controller只负责参数接收和结果返回,Service负责业务逻辑,Mapper负责数据库操作。举个例子,课程上下架这个操作,Controller接收课程ID,Service层做状态校验和数据更新,Mapper层执行更新SQL。这样做的好处是业务逻辑可以复用,后面再加一个“批量上下架”的接口,只需要在Service层加方法,不用重写SQL。

接口设计遵循RESTful风格,资源用名词表示,动作用HTTP方法表达:

请求方法路径作用
POST/api/auth/login登录
GET/api/courses课程列表
POST/api/courses新增课程
PUT/api/courses/{id}更新课程
DELETE/api/courses/{id}删除课程
GET/api/courses/{id}/chapters课程章节列表
GET/api/users/{id}/learning-records用户学习记录

统一返回结构也是前后端分离项目必须处理的。我习惯用Result对象包装所有接口返回值,包含code、message、data三个字段。code为200表示成功,非200表示失败。这样前端拦截器可以统一处理错误提示和登录失效,不用每个接口单独判断。

2.2 数据库怎么设计才扛得住真实业务

数据库设计是我在这个项目里花时间最多的地方,也是最能体现功力的部分。在线教育系统的核心表,我梳理出来大概有这几张:用户表、角色表、用户角色关联表、课程分类表、课程表、章节表、学习记录表、订单表。

用户表要区分管理员、讲师、学员,但我不建议在用户表里直接用type字段走天下。更合理的做法是做好角色关联,一个用户可能既是讲师也是某个课程的学员。我用的方案是独立的用户表加用户角色关联表,用户表存基础账号信息,角色表存角色定义,中间表把两者关联起来。这样后续要扩展角色权限,不需要改表结构。

课程表是业务的中心,字段上除了课程名称、封面图、简介、价格这些基础信息,还有几个关键字段需要特别设计:

  • status:课程状态,草稿、已上架、已下架。用数字表示,避免字符串在不同地方写法不一致。
  • teacher_id:归属讲师的用户ID。这里建议存普通索引,不要建外键约束,在线教育系统查询频繁,外键约束会影响插入和删除性能。
  • category_id:课程分类ID。分类一般是树形结构,为了简化处理,第一版可以只做一级分类,后续再扩展父子关系。
  • publish_time:上架时间,用于列表排序和运营数据分析。

章节表要挂在课程下面,包含章节标题、排序号、视频地址、是否免费试看。排序号非常重要,章节本来就是有序的,不能依赖创建时间排序,必须显式维护sort字段。免费试看字段在课程详情页和试看逻辑中都会用到。

学习记录表是我特别想强调的。真实场景中,学员学习课程会产生大量记录,这张表需要同时关联用户、章节、课程,再用一个watch_time记录看到第几秒,用status标记是否完成。每次前端上报学习进度都会更新这张表,所以它是全系统写入频率最高的表之一,要建联合索引(user_id, chapter_id),避免查询时全表扫描。

订单表在初版可以做成简单的报名记录表,包含用户ID、课程ID、支付金额、订单状态。纯后台管理场景下,重点是把“用户买了哪些课程”这个关系查清楚,方便做学习记录的权限校验。支付流程的复杂度可以放到第二阶段再考虑。

所有业务表我都保留了三个通用字段:create_time、update_time、deleted。逻辑删除用0和1表示,而不是物理删除数据。在线教育后台误删操作的场景不少,比如课程下架时如果物理删了,运营想恢复就麻烦了。逻辑删除让数据始终可追溯,查询时统一加WHERE deleted = 0条件即可。

2.3 模块划分与核心业务流程

整个系统的模块划分,我按业务域来拆,不按技术层拆。每个模块是一个独立的包结构,包含自己的Controller、Service、Mapper。主要有这几个模块:

  • 认证授权模块:登录、JWT生成与校验、权限注解。
  • 用户管理模块:用户CRUD、角色分配、讲师审核。
  • 课程管理模块:课程、章节、分类的维护。
  • 学习模块:学习记录上报、课程进度查询。
  • 统计模块:简单的数据统计接口,比如课程报名人数、学员完成率。

核心业务流程里,最值得理清楚的一条是“学员学习课程”。学员登录后获取自己的课程列表,进入课程详情,查看章节列表。前端在视频播放时每隔一段时间上报学习进度,后端收到请求后更新学习记录表。如果某章节已经达到完成阈值,就把状态标记为已完成。课程总体进度在列表页通过汇总该课程下已完成章节数除以总章节数来计算。

这个流程看起来简单,但里面涉及并发和幂等的问题:如果前端一秒上报一次,后端怎么做去重?如果用户断网重连,学习记录会不会重复插入?我后面在踩坑部分会详细说解决办法。这里先记住一个原则:学习记录表不能无脑每次插入新数据,而是先查再更新,最好用唯一索引兜底。

3. 核心功能实操:从0到1跑起来

3.1 环境准备与项目初始化

先把环境准备好。后端需要JDK 8或11、Maven 3.6以上版本、MySQL 5.7或8.0。前端需要Node.js 14以上,npm 6以上。如果机器上装的是新版本,比如JDK 17,也没问题,只要对应SpringBoot版本支持。我这边用的SpringBoot 2.7.x,兼容JDK 8到17,稳一点。

后端项目初始化推荐直接在IDEA里新建Spring Initializr项目,选好JDK版本和依赖。需要引入的依赖有:spring-boot-starter-web、mybatis-spring-boot-starter、mysql-connector-java、lombok、spring-boot-starter-validation、jjwt。

application.yml配置是最开始就要写对的:

server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/edu_platform?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.edu.entity configuration: map-underscore-to-camel-case: true

map-underscore-to-camel-case这个配置很容易被忽略,但它非常关键。数据库字段是create_time这种下划线风格,Java实体类是createTime这种驼峰风格,开启这个配置后MyBatis会自动映射。否则你就要在每条SQL里写别名,非常痛苦。

前端项目初始化用Vue CLI或Vite都可以。我的建议是直接用Vue CLI创建,生态稳定,模板工程对后台管理系统非常友好。创建命令:

npm install -g @vue/cli vue create edu-frontend

创建时选手动配置,勾选Vue Router、Vuex和Axios。进入项目后,先安装Element UI作为后台管理界面的组件库:

npm install element-ui npm install axios

Element UI的表格、表单、弹窗、分页组件都非常成熟,做管理后台几乎不用自己造轮子。

3.2 后端核心模块:JWT认证与拦截

管理后台的安全控制是第一优先级。这套系统用的是JWT方案:用户登录成功后,后端生成一个带过期时间的Token返回给前端,前端每次请求都把这个Token放在HTTP请求头里,后端拦截器校验Token的合法性并解析出用户信息。

先定义一个简单的JWT工具类:

@Component public class JwtUtil { @Value("${jwt.secret}") private String secret; @Value("${jwt.expire}") private Long expire; public String generateToken(Integer userId, String username) { return Jwts.builder() .setSubject(username) .claim("userId", userId) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + expire)) .signWith(SignatureAlgorithm.HS256, secret) .compact(); } public Claims parseToken(String token) { return Jwts.parser() .setSigningKey(secret) .parseClaimsJws(token) .getBody(); } }

登录接口的Service层逻辑:根据用户名查到用户,校验密码是否一致,校验通过就生成Token返回。密码存储不能是明文,至少要用MD5加盐或者BCrypt加密。我在项目里用的是BCrypt,Spring Security的加密工具类可以直接调,比自写MD5稳妥得多。

拦截器是JWT校验的核心。我写了一个AuthInterceptor,在请求进入Controller之前拦截:

public class AuthInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (OPTIONS.equals(request.getMethod())) { return true; } String token = request.getHeader("Authorization"); if (StringUtils.hasText(token) && token.startsWith("Bearer ")) { token = token.substring(7); } try { Claims claims = JwtUtil.parseToken(token); request.setAttribute("userId", claims.get("userId")); return true; } catch (Exception e) { response.setStatus(401); response.setContentType("application/json"); response.getWriter().write("{\"code\":401,\"message\":\"登录已过期\"}"); return false; } } }

这里有两个容易踩的坑:第一,OPTIONS预检请求必须放行,否则前端跨域请求会被拦截器挡掉。第二,拦截器里校验失败时要直接返回JSON,而不是让Spring再走异常处理流程,避免前端拿到一堆HTML错误页面。

权限控制方面,我用了自定义注解@RequireRole,标注在Controller方法上,比如讲师管理接口只允许ROLE_ADMIN访问。拦截器解析Token后拿到用户ID,再查一次数据库得到角色信息,校验是否符合注解要求。这个方法虽然每请求多查一次数据库,但胜在实现简单清晰,对于后端管理系统的并发量来说完全够用。

3.3 前端页面对接与接口联调

前端的核心是把请求封装好,后续页面调用时不用重复处理Token和错误提示。我在src目录下建了一个api文件夹,统一封装axios实例:

import axios from 'axios' import { Message } from 'element-ui' import router from '../router' const service = axios.create({ baseURL: process.env.VUE_APP_BASE_API, timeout: 10000 }) service.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) { config.headers['Authorization'] = 'Bearer ' + token } return config }) service.interceptors.response.use( response => { const res = response.data if (res.code !== 200) { Message.error(res.message || '请求失败') return Promise.reject(new Error(res.message)) } return res }, error => { if (error.response && error.response.status === 401) { router.push('/login') } Message.error(error.message) return Promise.reject(error) } ) export default service

Vue页面开发时最常用的是表格加弹窗表单的组合。以课程管理页面为例,页面加载时调用GET /api/courses获取列表,点击新增按钮弹出表单对话框,提交时调用POST /api/courses。这种模式几乎覆盖了后台管理系统80%的页面。

跨域问题是前后端分离联调时最常见的问题。开发环境下,Vue项目默认跑在8080端口,后端跑在8080端口,这两者之间就是跨域请求。我用的解决办法是在后端加CORS配置: ```java @Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOriginPatterns("*") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .maxAge(3600); } }

生产环境会更好处理,前端打包成静态文件后由Nginx提供服务,然后用反向代理把/api路径转发到后端服务。这样浏览器看到的始终是同源请求,不需要后端处理跨域。开发环境用CORS配置,生产环境用Nginx反向代理,这是比较标准的做法。

3.4 学习进度上报接口的实现细节

学习进度接口是整个系统里最贴近真实使用场景的一个功能,值得单独拿出来讲。前端在视频播放时,每隔15秒调一次上报接口,传递userId、chapterId、watchTime、duration。后端收到请求后,先查询是否已经有这条学习记录,没有就插入,有就更新watchTime。

学习记录完成状态的判定不能死板。我的做法是:当watchTime大于等于duration的90%,或者当前端主动标记manualFinish为true时,把status改为完成。为什么是90%而不是100%?因为用户很可能跳过结尾演职人员表,或者视频最后几秒网络卡顿,强制要求100%反而会让用户觉得系统很死板。

事务在这里要注意,先查再插再更新,整体操作要加@Transactional,防止插入记录和更新状态之间出现异常导致数据不一致。具体代码逻辑大概是:

@Transactional public void reportLearningProgress(ProgressRequest request) { LearningRecord record = learningRecordMapper.selectByUserAndChapter( request.getUserId(), request.getChapterId()); if (record == null) { record = new LearningRecord(); record.setUserId(request.getUserId()); record.setChapterId(request.getChapterId()); record.setWatchTime(request.getWatchTime()); record.setStatus(0); learningRecordMapper.insert(record); } else { record.setWatchTime(request.getWatchTime()); if (request.getWatchTime() >= request.getDuration() * 0.9) { record.setStatus(1); } learningRecordMapper.update(record); } }

并发上报时,如果我第一次请求和第二次请求几乎同时到达,可能会插入两条记录。更稳妥的做法是在数据库层面给(user_id, chapter_id)加上联合唯一索引,让数据库帮你兜底,重复插入直接报错,代码里再捕获异常做更新处理。算法层面的幂等控制永远没有数据库约束可靠,这个是我实际测试出来的教训。

4. 真实开发中踩过的坑与排查技巧

4.1 事务失效和数据一致性问题

在线教育系统里事务用得多,但很多人写事务时都会忽略几个让人头疼的场景。最典型的,在同一个类里,A方法调用B方法,B方法上面标了@Transactional,但是事务不生效。原因很简单,Spring的事务是通过AOP代理实现的,类内部的方法调用走的是this,不会经过代理对象,事务注解就被跳过了。解决办法是把需要事务的方法放到另一个Service类里,或者自己从容器中获取代理对象再调用。

另一个常见问题是事务内做远程调用。比如学习进度上报时,如果先是更新学习记录,再调用短信服务通知用户,短信服务响应很慢,事务会一直持有数据库连接不释放,最终导致连接池耗尽。我的处理原则是:事务里只做数据库操作,远程调用放到事务提交之后通过事件或异步方式处理。

数据一致性还体现在课程状态上。课程删除时,如果直接把课程表里的记录删除,那么学员的学习记录表里就有大量悬空数据,课程列表查询也会出问题。这就是为什么我在前面强调逻辑删除字段。课程下架的时候只更新deleted=1,学习记录表里的数据还能正常关联到课程历史快照信息。

4.2 接口性能瓶颈与优化

这套系统在数据量小的时候跑起来很顺畅,一旦数据库里课程、用户、学习记录都到了几十万条,性能问题就会暴露。最让我印象深刻的场景是课程列表页:每次查询课程列表都要统计报名人数和章节数,如果使用写死的SQL把所有数据都拆出来,N+1查询问题马上就来了。

N+1查询的意思是,先用一条SQL查出课程列表,再对每个课程循环查询学员数量。课程越多,查询次数越多,接口响应时间直线上升。解决办法是用联表查询或子查询一次拿到所有需要的数据。MyBatis里写一个聚合SQL就能解决,比如:

SELECT c.id, c.title, (SELECT COUNT(*) FROM orders o WHERE o.course_id = c.id) AS student_count FROM course c WHERE c.deleted = 0

分页查询的深度分页问题也要注意。当使用LIMIT 100000, 20这种查询时,MySQL还是要扫描前100000条记录,效率很低。优化思路有两种:一是使用WHERE id > lastId的方式做游标分页,适合移动端无限滚动场景;二是给排序字段和筛选条件建立联合索引,让数据库尽量走索引覆盖。后台表格用普通分页就好,前端一览式的数据列表建议用游标分页。

学习记录表写入频繁,建议按用户维度做缓存。用户打开课程列表时,不需要实时统计每个课程的学习进度,可以把进度数据缓存到Redis里,设置5分钟过期。用户每次上报进度时更新一次Redis,页面展示时读缓存,只有管理员看统计报表时才查数据库。这样可以显著降低数据库压力。

4.3 部署上线时的细节处理

本地开发跑通了,部署又是半个坑。SpringBoot后端打包时要特别注意配置文件。开发环境连的是本地数据库,服务器上用的是云数据库,不能把本地数据库地址直接打包进去。我建议把application.yml拆成三个环境:application-dev.yml、application-test.yml、application-prod.yml。打包时通过spring.profiles.active指定环境,部署命令类似:

java -jar edu-backend.jar --spring.profiles.active=prod

前端部署要处理的是路由模式。Vue Router如果用了history模式,Nginx需要配置try_files,把请求都重定向到index.html。否则前端页面一刷新,Nginx就会报404,这个问题太经典了。Nginx配置大概长这样:

location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; } location /api { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; }

生产环境还有一个容易忽略的点:MySQL的时区问题。如果后端配置了serverTimezone=Asia/Shanghai,但MySQL服务器用的是UTC,查出来直接更新到数据库,你会发现时间晚了8小时。最好的办法是把MySQL的全局时区也设置为东八区,同时在后端配置里保持一致,双重确认。

4.4 前端联调中的常见问题速查

我在联调阶段遇到过不少前端问题,整理成一张表:

现象原因解决办法
请求返回401但登录页没跳拦截器里处理的是response error,但HTTP状态码可能被axios拦了检查response拦截器的error分支
表格数据不显示后端返回的字段和前端表头字段不一致统一使用驼峰命名,开启MyBatis驼峰映射
表单提交后列表不刷新提交成功后没有重新调用列表接口在成功回调里重新查询当前页数据
上传图片失败跨域请求被Nginx或CORS配置拦截检查Nginx的client_max_body_size
Token过期后页面卡死401后没有清理本地存储和跳转拦截器里清空localStorage后跳登录页

联调是前后端项目里最容易扯皮也最容易拖时间的环节。我的习惯是先在前端Mock数据把页面写完,所有字段类型和接口路径都按真实约定来,后端接口出了之后把Mock地址切到真实地址,冲突就会少很多。

5. 从能用做到好用:扩展与经验沉淀

5.1 演示项目到线上项目差在哪

很多开发者做的在线教育系统,功能看着都有,但离真正上线还有一段距离。最大的差距在容错和可观测性。演示项目里接口挂了只会刷新重来,线上系统要求的是接口异常时有日志、有告警、有兜底数据。我在后期给这套系统加了全局异常处理类,统一捕获业务异常和未知异常,记录日志并返回友好提示。加上操作日志模块,后台管理员每次上下架课程、修改用户信息都会记录操作者和时间,出了问题能追溯到人。

数据层面,线上系统还要求备份策略。MySQL备份不只是mysqldump命令,而是要有全量备份加增量日志。我建议至少每天全量备份一次,同时开启binlog。这样即使误操作删了数据,也能恢复到指定的时间点,对在线教育这种运营人员直接操作系统的情况非常关键。

5.2 个人调试效率技巧

几个能明显提升开发效率的小习惯。后端接口测试,与其频繁起一个前端页面去点,不如直接用接口测试工具把所有接口集合保存下来。后端改完接口,跑一遍集合就能知道有没有破坏已有功能。前端调试时,多用浏览器的Network面板看接口耗时和响应体,比反复加console快得多。

SQL排查我习惯在MyBatis的XML里写注释,把业务背景和参数含义写明。半年后自己回头维护代码,或者让另一个同事接手,这些注释的价值远大于代码层的注释。遇到过太多次,写的SQL自己都看不懂的情况。

联调时前端报错,第一反应不是去怀疑后端逻辑,而是先打开浏览器控制台把Full Request URL复制出来,直接在接口工具里重放一遍。如果接口工具返回正常,那就是前端传参问题;如果接口工具也报错,再往后端查。这个排除法能省下大量沟通成本。

5.3 后续可以怎么扩展

这套系统留了几个明确的扩展点。课程播放页面可以接入真正的前端视频播放器,配合学习记录接口做续播功能。权限方面,可以把方法级别的注解权限升级成细粒度按钮权限,前端根据用户权限动态渲染按钮。统计报表可以引入定时任务,每天早上汇总前一天的学员活跃、课程报名数据,生成运营日报。

如果后续还需要支持多人同时观看直播课,那就要考虑引入消息推送和WebRTC相关技术,这部分和当前管理后台是相对独立的模块。架构上保持模块清晰,技术升级不会影响已有功能。

回头再看,这个项目的价值不只是把SpringBoot+Vue这套技术栈跑通,而是通过在线教育这个真实业务场景,把权限、事务、性能、部署这些后端开发的硬骨头都过了一遍。我在实际调试过程中最大的体会是,很多问题都是到数据量上去之后才暴露出来的,单纯写单测或者跑小样本数据根本发现不了。所以做这类管理系统,一定在一开始就按真实数据量去设计表结构和索引,不要等线上卡死才回头改模型。

最后分享一个小技巧:课程表和学习记录表这种高频访问的表,主键不要用UUID字符串,用自增整型主键就好。SQL里做JOIN时整型主键的索引效率和存储空间都远优于字符串。一套字段设计要经得起未来几年的数据增长,这才是后端开发的核心功底。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询