guoyu/_已清理文件备份_周六 22512/md/导入性能优化方案.md

324 lines
6.4 KiB
Markdown
Raw Normal View History

2025-12-06 20:11:36 +08:00
# 导入性能优化方案
## ❌ **问题分析**
### **问题1SQL执行慢每条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)
```
---
### **问题3UPDATE SQL字段重复**
```sql
update sys_user
SET prison_name = ?, -- 第一次
prison_area = ?,
...
prison_name = ?, -- 第二次重复!
prison_area = ?, -- 重复!
...
```
---
## ✅ **解决方案**
### **方案1修复班级分配逻辑**
#### **当前逻辑(错误):**
```java
// 1. 将所有旧班级设为status=0
for (StudyStudentClass old : oldClassList) {
old.setStatus(0);
studentClassMapper.updateStudentClass(old); // 慢查询
}
// 2. 插入新班级
studentClassMapper.insertStudentClass(studentClass); // 失败Duplicate
```
#### **优化后逻辑(正确):**
```java
// 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添加数据库索引**
#### **检查当前索引:**
```sql
SHOW INDEX FROM sys_user;
SHOW INDEX FROM student_class;
```
#### **建议添加的索引:**
```sql
-- 用户表:按信息编号查询
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` 是否有重复字段:
```xml
<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使用批量操作**
#### **当前:逐条处理**
```java
for (SysUser user : userList) {
userMapper.insertUser(user); // 每次1.2秒
}
```
#### **优化:批量插入**
```java
// 批量插入每批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添加数据库索引**
```sql
-- 连接数据库
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优化数据库配置**
检查数据库连接池配置:
```yaml
# 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错误
---
## 🧪 **测试步骤**
1. **准备测试数据**
```bash
python generate_test_data.py # 生成100条
```
2. **执行导入**
- 记录开始时间
- 导入test_data.xlsx
- 记录结束时间
3. **查看日志**
```bash
# 搜索慢查询
grep "slow sql" logs/app.log
# 搜索错误
grep "Duplicate entry" logs/app.log
# 计算平均时间
```
4. **验证结果**
- 检查导入成功数量
- 检查班级分配是否正确
- 检查是否有错误
---
## 💡 **后续优化建议**
### **1. 使用Redis缓存**
- 缓存班级映射
- 缓存角色ID
- 减少数据库查询
### **2. 异步处理**
- 使用消息队列
- 导入任务放入队列
- 后台异步处理
### **3. 分片导入**
- 大文件分片上传
- 每片独立处理
- 提高并发性
### **4. 数据库优化**
- 分表分库
- 读写分离
- 使用更快的存储引擎
---
## ✅ **总结**
### **核心问题**
1. 班级分配逻辑错误导致Duplicate
2. UPDATE SQL慢查询
3. 缺少必要的数据库索引
### **解决方法**
1. 修复班级分配逻辑
2. 检查并优化SQL
3. 添加数据库索引
### **预期效果**
- 性能提升10倍以上
- 无Duplicate错误
- 导入更流畅