6.4 KiB
6.4 KiB
导入性能优化方案
❌ 问题分析
问题1:SQL执行慢(每条1.2秒)
slow sql 1164 millis. update sys_user
slow sql 1165 millis. update student_class
原因:
- 每条记录更新需要1.2秒,100条就需要2分钟
- UPDATE语句中字段重复
- 可能缺少数据库索引
问题2:班级分配逻辑错误
Duplicate entry '234-17' for key 'student_class.uk_student_class'
原因:
- 先UPDATE旧班级status=0
- 再INSERT新班级记录
- 如果新旧班级ID相同,INSERT失败(唯一键冲突)
日志分析:
14:58:25.593 UPDATE student_class SET status=0 WHERE id=4642 (1.2秒)
14:58:26.760 INSERT student_class (234, 17, ...) (失败:Duplicate)
问题3:UPDATE SQL字段重复
update sys_user
SET prison_name = ?, -- 第一次
prison_area = ?,
...
prison_name = ?, -- 第二次重复!
prison_area = ?, -- 重复!
...
✅ 解决方案
方案1:修复班级分配逻辑
当前逻辑(错误):
// 1. 将所有旧班级设为status=0
for (StudyStudentClass old : oldClassList) {
old.setStatus(0);
studentClassMapper.updateStudentClass(old); // 慢查询
}
// 2. 插入新班级
studentClassMapper.insertStudentClass(studentClass); // 失败:Duplicate
优化后逻辑(正确):
// 1. 检查是否已在该班级
boolean classUpdated = false;
for (StudyStudentClass old : oldClassList) {
if (old.getClassId().equals(classId)) {
// 已在该班级,只需确保状态为活跃
if (old.getStatus() != 1) {
old.setStatus(1);
studentClassMapper.updateStudentClass(old);
}
classUpdated = true;
break;
}
}
// 2. 如果不在该班级,禁用其他班级
if (!classUpdated) {
for (StudyStudentClass old : oldClassList) {
if (old.getStatus() == 1) {
old.setStatus(0);
studentClassMapper.updateStudentClass(old);
}
}
}
// 3. 如果不在该班级,插入新记录
if (!classUpdated) {
studentClassMapper.insertStudentClass(studentClass);
}
优势:
- ✅ 避免Duplicate错误
- ✅ 减少不必要的UPDATE
- ✅ 逻辑更清晰
方案2:添加数据库索引
检查当前索引:
SHOW INDEX FROM sys_user;
SHOW INDEX FROM student_class;
建议添加的索引:
-- 用户表:按信息编号查询
CREATE INDEX idx_user_name ON sys_user(user_name);
-- 学员班级表:按学员ID查询
CREATE INDEX idx_student_id ON student_class(student_id);
-- 学员班级表:按状态查询
CREATE INDEX idx_status ON student_class(status);
方案3:检查UPDATE SQL(需要修复Mapper)
检查 SysUserMapper.xml 中的 updateUser 是否有重复字段:
<update id="updateUser">
update sys_user
SET prison_name = #{prisonName},
prison_area = #{prisonArea},
...
<!-- 检查是否重复 -->
prison_name = #{prisonName}, <!-- 重复! -->
prison_area = #{prisonArea} <!-- 重复! -->
where user_id = #{userId}
</update>
方案4:使用批量操作
当前:逐条处理
for (SysUser user : userList) {
userMapper.insertUser(user); // 每次1.2秒
}
优化:批量插入
// 批量插入(每批50条)
List<SysUser> batch = new ArrayList<>();
for (SysUser user : userList) {
batch.add(user);
if (batch.size() >= 50) {
userMapper.batchInsertUsers(batch);
batch.clear();
}
}
if (!batch.isEmpty()) {
userMapper.batchInsertUsers(batch);
}
🔧 立即修复步骤
步骤1:修复班级分配逻辑
文件:StudyClassUserServiceImpl.java
位置:第1110-1126行(更新用户时的班级分配)
需要补充完整的班级分配逻辑(类似新增用户的逻辑)
步骤2:检查并修复UPDATE SQL
文件:SysUserMapper.xml
搜索:<update id="updateUser">
删除重复的字段设置
步骤3:添加数据库索引
-- 连接数据库
USE ry_study;
-- 检查现有索引
SHOW INDEX FROM sys_user WHERE Key_name = 'idx_user_name';
SHOW INDEX FROM student_class WHERE Key_name = 'idx_student_id';
-- 添加缺失的索引
CREATE INDEX idx_user_name ON sys_user(user_name) IF NOT EXISTS;
CREATE INDEX idx_student_id ON student_class(student_id) IF NOT EXISTS;
CREATE INDEX idx_status ON student_class(status) IF NOT EXISTS;
步骤4:优化数据库配置
检查数据库连接池配置:
# application.yml
spring:
datasource:
druid:
initial-size: 10
min-idle: 10
max-active: 50 # 增加最大连接数
max-wait: 60000
##⚡ 预期性能提升
修复前:
- 100条数据:约2-3分钟
- 每条平均:1.2-1.8秒
修复后:
- 100条数据:约10-20秒
- 每条平均:0.1-0.2秒
提升:10倍以上
📋 快速检查清单
- 修复班级分配逻辑
- 检查UPDATE SQL是否有重复字段
- 添加数据库索引
- 测试导入100条数据
- 查看SQL慢查询日志
- 确认无Duplicate错误
🧪 测试步骤
- 准备测试数据
python generate_test_data.py # 生成100条
-
执行导入
- 记录开始时间
- 导入test_data.xlsx
- 记录结束时间
-
查看日志
# 搜索慢查询
grep "slow sql" logs/app.log
# 搜索错误
grep "Duplicate entry" logs/app.log
# 计算平均时间
- 验证结果
- 检查导入成功数量
- 检查班级分配是否正确
- 检查是否有错误
💡 后续优化建议
1. 使用Redis缓存
- 缓存班级映射
- 缓存角色ID
- 减少数据库查询
2. 异步处理
- 使用消息队列
- 导入任务放入队列
- 后台异步处理
3. 分片导入
- 大文件分片上传
- 每片独立处理
- 提高并发性
4. 数据库优化
- 分表分库
- 读写分离
- 使用更快的存储引擎
✅ 总结
核心问题
- 班级分配逻辑错误导致Duplicate
- UPDATE SQL慢查询
- 缺少必要的数据库索引
解决方法
- 修复班级分配逻辑
- 检查并优化SQL
- 添加数据库索引
预期效果
- 性能提升10倍以上
- 无Duplicate错误
- 导入更流畅