xinli/系统优化说明文档.md
2025-12-02 15:12:55 +08:00

11 KiB
Raw Blame History

系统优化说明文档

优化日期

2025年12月1日

优化内容概览

优化1大模型API - 从远程改为本地

优化2用户导入性能 - 批量处理优化


优化1大模型API配置修改

问题背景

系统使用远程Kimi API进行AI分析存在以下问题

  • 依赖外部网络,可能不稳定
  • 需要API Key有安全风险
  • 调用次数可能受限
  • 成本较高

修改方案

将远程API改为本地大模型如Ollama支持离线使用。

修改文件

1. xinli-ui/src/views/psychology/report/comprehensive.vue

// 修改前
API_URL: 'https://api.moonshot.cn/v1/chat/completions',
API_KEY: 'sk-U9fdriPxwBcrpWW0Ite3N0eVtX7VxnqqqYUIBAdWd1hgEA9m',
MODEL: 'moonshot-v1-32k'

// 修改后
API_URL: 'http://localhost:11434/v1/chat/completions',
API_KEY: '',  // 本地模型不需要API Key
MODEL: 'qwen2.5:7b'  // 根据实际使用的本地模型修改

2. xinli-ui/src/views/psychology/report/index.vue

// 修改前
const API_URL = 'https://api.moonshot.cn/v1/chat/completions';
const API_KEY = 'sk-U9fdriPxwBcrpWW0Ite3N0eVtX7VxnqqqYUIBAdWd1hgEA9m';
const MODEL = 'moonshot-v1-32k';

// 修改后
const API_URL = 'http://localhost:11434/v1/chat/completions';
const API_KEY = '';  // 本地模型不需要API Key
const MODEL = 'qwen2.5:7b';  // 根据实际使用的本地模型修改

3. xinli-ui/src/views/psychology/report/detail.vue

// 修改前
const API_URL = 'https://api.moonshot.cn/v1/chat/completions';
const API_KEY = 'sk-U9fdriPxwBcrpWW0Ite3N0eVtX7VxnqqqYUIBAdWd1hgEA9m';
const MODEL = 'moonshot-v1-32k';

// 修改后
const API_URL = 'http://localhost:11434/v1/chat/completions';
const API_KEY = '';  // 本地模型不需要API Key
const MODEL = 'qwen2.5:7b';  // 根据实际使用的本地模型修改

本地模型部署

使用Ollama推荐

  1. 安装Ollama
# Windows
# 下载并安装https://ollama.com/download

# Linux
curl -fsSL https://ollama.com/install.sh | sh

# MacOS
brew install ollama
  1. 下载模型
# 下载Qwen2.5模型(推荐,支持中文)
ollama pull qwen2.5:7b

# 或其他中文模型
ollama pull qwen:14b
ollama pull chatglm3:6b
  1. 启动服务
ollama serve

服务会在 http://localhost:11434 启动

  1. 测试
curl http://localhost:11434/api/generate -d '{
  "model": "qwen2.5:7b",
  "prompt": "你好"
}'

修改后的优势

  • 离线可用:不依赖外部网络
  • 无需API Key:本地部署,无需管理密钥
  • 无调用限制:可以无限次调用
  • 数据安全:数据不会发送到外部
  • 成本降低无API调用费用
  • 可定制:可以选择不同的本地模型

注意事项

  1. 模型选择:根据服务器配置选择合适的模型

    • 7B模型需要至少8GB内存
    • 14B模型需要至少16GB内存
  2. 端口配置:确保11434端口未被占用

  3. 性能:本地模型响应速度取决于硬件性能

  4. 模型更换:如果使用其他模型,需要同步修改代码中的MODEL参数


优化2用户导入性能优化

性能问题分析

优化前的问题

导入3000条用户数据需要10几分钟原因

  1. 逐条查询3000次数据库查询
for (PsyUserProfile profile : profileList) {
    // 每条都要查询一次数据库
    PsyUserProfile existProfile = profileMapper.selectProfileByInfoNumber(profile.getInfoNumber());
}
  1. 逐条插入/更新3000次数据库写操作
for (PsyUserProfile profile : profileList) {
    if (existProfile == null) {
        this.insertProfile(profile);  // 单条插入
    } else {
        this.updateProfile(profile);  // 单条更新
    }
}

总计: 6000+ 次数据库操作!

性能瓶颈

  • N+1问题:每条数据都触发一次查询
  • 网络开销:每次操作都有网络往返
  • 事务开销:每次操作都可能有事务提交
  • 索引维护:每次插入都要更新索引

优化方案

核心思路

逐条处理改为批量处理

优化步骤

步骤1批量查询1次查询替代3000次

// 收集所有infoNumber
List<String> infoNumbers = profileList.stream()
    .map(PsyUserProfile::getInfoNumber)
    .collect(Collectors.toList());

// 一次性查询所有已存在的记录
List<PsyUserProfile> existingProfiles = 
    profileMapper.selectProfilesByInfoNumbers(infoNumbers);

步骤2数据分类

List<PsyUserProfile> toInsertList = new ArrayList<>();  // 待插入
List<PsyUserProfile> toUpdateList = new ArrayList<>();  // 待更新

for (PsyUserProfile profile : validProfiles) {
    if (existingMap.containsKey(profile.getInfoNumber())) {
        toUpdateList.add(profile);
    } else {
        toInsertList.add(profile);
    }
}

步骤3批量插入每批500条

int batchSize = 500;
for (int i = 0; i < toInsertList.size(); i += batchSize) {
    List<PsyUserProfile> batch = toInsertList.subList(i, i + batchSize);
    profileMapper.batchInsertProfiles(batch);  // 批量插入500条
}

步骤4批量更新每批500条

for (int i = 0; i < toUpdateList.size(); i += batchSize) {
    List<PsyUserProfile> batch = toUpdateList.subList(i, i + batchSize);
    profileMapper.batchUpdateProfiles(batch);  // 批量更新500条
}

修改的文件

1. Mapper接口 - PsyUserProfileMapper.java

新增方法:

/**
 * 批量查询档案(根据信息编号列表)
 */
public List<PsyUserProfile> selectProfilesByInfoNumbers(List<String> infoNumbers);

/**
 * 批量插入档案
 */
public int batchInsertProfiles(List<PsyUserProfile> profileList);

/**
 * 批量更新档案
 */
public int batchUpdateProfiles(List<PsyUserProfile> profileList);

2. Mapper XML - PsyUserProfileMapper.xml

批量查询SQL:

<select id="selectProfilesByInfoNumbers" resultMap="PsyUserProfileResult">
    SELECT * FROM psy_user_profile
    WHERE info_number IN
    <foreach item="infoNumber" collection="list" open="(" separator="," close=")">
        #{infoNumber}
    </foreach>
</select>

批量插入SQL:

<insert id="batchInsertProfiles">
    INSERT INTO psy_user_profile(...) VALUES
    <foreach item="item" collection="list" separator=",">
        (#{item.userId}, #{item.infoNumber}, ...)
    </foreach>
</insert>

批量更新SQL:

<update id="batchUpdateProfiles">
    <foreach item="item" collection="list" separator=";">
        UPDATE psy_user_profile SET ... WHERE profile_id = #{item.profileId}
    </foreach>
</update>

3. Service实现 - PsyUserProfileServiceImpl.java

优化前:

for (PsyUserProfile profile : profileList) {
    // 3000次查询
    PsyUserProfile existProfile = profileMapper.selectProfileByInfoNumber(...);
    
    if (existProfile == null) {
        this.insertProfile(profile);  // 3000次插入
    } else {
        this.updateProfile(profile);  // 或3000次更新
    }
}

优化后:

// 1. 批量查询1次
List<PsyUserProfile> existingProfiles = 
    profileMapper.selectProfilesByInfoNumbers(infoNumbers);

// 2. 分类处理
List<PsyUserProfile> toInsertList = ...;
List<PsyUserProfile> toUpdateList = ...;

// 3. 批量插入6次每批500条
profileMapper.batchInsertProfiles(toInsertList);

// 4. 批量更新6次每批500条
profileMapper.batchUpdateProfiles(toUpdateList);

性能提升对比

操作 优化前 优化后 提升
数据库查询 3000次 1次 3000倍
数据库插入 3000次 6次500条/批) 500倍
总操作数 6000次 7次 857倍
导入时间 10-15分钟 10-30秒 20-50倍

预期效果

  • 3000条数据从10-15分钟 → 10-30秒
  • 10000条数据:预计 30秒-1分钟
  • 数据库压力降低减少99%的数据库操作
  • 用户体验提升:导入响应更快

容错机制

  1. 批量失败回退:如果批量插入失败,自动回退到逐条插入
try {
    profileMapper.batchInsertProfiles(batch);
} catch (Exception e) {
    // 批量失败,尝试逐条插入
    for (PsyUserProfile profile : batch) {
        this.insertProfile(profile);
    }
}
  1. 进度跟踪:保留导入进度显示功能
  2. 错误记录:详细记录每条失败的数据

数据库配置建议

为了支持批量更新需要在数据库连接URL中添加

# application.yml 或 application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/xinli?allowMultiQueries=true

allowMultiQueries=true 允许在一个语句中执行多条SQL用于批量更新

测试验证

测试步骤

  1. 准备3000条测试数据的Excel文件
  2. 进入"用户档案" → 点击"导入"
  3. 选择Excel文件上传
  4. 观察导入进度和时间

预期结果

  • 导入时间10-30秒
  • 进度显示:实时更新
  • 成功率100%(数据正确情况下)
  • 数据完整:所有字段正确导入

部署说明

前端部署

cd xinli-ui
npm run build
# 将dist目录部署到nginx

后端部署

cd ry-xinli
mvn clean package
# 重启Spring Boot应用
java -jar ry-xinli-admin/target/ry-xinli.jar

数据库配置

确保数据库连接URL包含 allowMultiQueries=true

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/xinli?allowMultiQueries=true&useUnicode=true&characterEncoding=utf-8&useSSL=false

总结

优化1大模型API

  • 移除远程API依赖
  • 改用本地Ollama
  • 降低成本,提升安全性

优化2用户导入

  • 批量查询3000次 → 1次
  • 批量插入3000次 → 6次
  • 导入时间10-15分钟 → 10-30秒
  • 性能提升:20-50倍

技术要点

  1. 批量处理替代逐条处理
  2. 减少数据库往返次数
  3. 使用MyBatis foreach批量操作
  4. 分批处理避免内存溢出
  5. 失败回退机制保证可靠性

注意事项

  1. 本地大模型需要单独部署Ollama
  2. 数据库需要开启allowMultiQueries
  3. 大批量数据建议分批导入每批不超过1万条
  4. 导入前建议备份数据库

附录

常见问题

Q1: 本地大模型响应慢怎么办? A:

  • 使用更小的模型如qwen2.5:3b
  • 升级服务器硬件(增加内存/CPU
  • 优化提示词,减少输出长度

Q2: 批量导入失败怎么办? A:

  • 查看错误日志
  • 检查数据格式是否正确
  • 确认数据库配置正确
  • 系统会自动回退到逐条导入

Q3: 能导入更多数据吗? A: 可以系统支持10万+数据导入,只是时间会更长。建议:

  • 1万条以内一次性导入
  • 1万-10万分批导入
  • 10万以上后台定时任务导入

Q4: 如何验证优化效果? A:

  • 准备相同的测试数据
  • 对比优化前后的导入时间
  • 检查数据库慢查询日志
  • 监控服务器资源使用情况