guoyu/log/导入性能优化方案.md

324 lines
6.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 导入性能优化方案
## ❌ **问题分析**
### **问题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错误
- 导入更流畅