guoyu/_已清理文件备份_周六 22512/md/导入性能优化方案.md
2025-12-06 20:11:36 +08:00

6.4 KiB
Raw Blame History

导入性能优化方案

问题分析

问题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字段重复

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错误

🧪 测试步骤

  1. 准备测试数据
python generate_test_data.py  # 生成100条
  1. 执行导入

    • 记录开始时间
    • 导入test_data.xlsx
    • 记录结束时间
  2. 查看日志

# 搜索慢查询
grep "slow sql" logs/app.log

# 搜索错误
grep "Duplicate entry" logs/app.log

# 计算平均时间
  1. 验证结果
    • 检查导入成功数量
    • 检查班级分配是否正确
    • 检查是否有错误

💡 后续优化建议

1. 使用Redis缓存

  • 缓存班级映射
  • 缓存角色ID
  • 减少数据库查询

2. 异步处理

  • 使用消息队列
  • 导入任务放入队列
  • 后台异步处理

3. 分片导入

  • 大文件分片上传
  • 每片独立处理
  • 提高并发性

4. 数据库优化

  • 分表分库
  • 读写分离
  • 使用更快的存储引擎

总结

核心问题

  1. 班级分配逻辑错误导致Duplicate
  2. UPDATE SQL慢查询
  3. 缺少必要的数据库索引

解决方法

  1. 修复班级分配逻辑
  2. 检查并优化SQL
  3. 添加数据库索引

预期效果

  • 性能提升10倍以上
  • 无Duplicate错误
  • 导入更流畅