325 lines
7.8 KiB
Markdown
325 lines
7.8 KiB
Markdown
|
|
# UPDATE 慢查询问题修复
|
|||
|
|
|
|||
|
|
## ❌ **问题现象**
|
|||
|
|
|
|||
|
|
导入100条数据需要很长时间,每条UPDATE需要1.5-1.6秒。
|
|||
|
|
|
|||
|
|
### **日志显示**
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
slow sql 1608 millis. update sys_user
|
|||
|
|
slow sql 1577 millis. update sys_user
|
|||
|
|
slow sql 1160 millis. update sys_user
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**预期时间:** <100ms
|
|||
|
|
**实际时间:** 1500ms+
|
|||
|
|
**慢了15倍!**
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🔍 **问题原因**
|
|||
|
|
|
|||
|
|
### **原因1:UPDATE SQL 字段重复设置(最严重)**
|
|||
|
|
|
|||
|
|
在 `SysUserMapper.xml` 的 `updateUser` 方法中,字段设置了**两次**:
|
|||
|
|
|
|||
|
|
```xml
|
|||
|
|
<!-- 第一次:340-350行 -->
|
|||
|
|
<if test="prisonName != null and prisonName != ''">prison_name = #{prisonName},</if>
|
|||
|
|
<if test="prisonArea != null and prisonArea != ''">prison_area = #{prisonArea},</if>
|
|||
|
|
<if test="ethnicity != null and ethnicity != ''">ethnicity = #{ethnicity},</if>
|
|||
|
|
<if test="educationLevel != null and educationLevel != ''">education_level = #{educationLevel},</if>
|
|||
|
|
<if test="crimeName != null and crimeName != ''">crime_name = #{crimeName},</if>
|
|||
|
|
<if test="sentenceTerm != null">sentence_term = #{sentenceTerm},</if>
|
|||
|
|
<if test="sentenceStartDate != null">sentence_start_date = #{sentenceStartDate},</if>
|
|||
|
|
<if test="sentenceEndDate != null">sentence_end_date = #{sentenceEndDate},</if>
|
|||
|
|
<if test="entryDate != null">entry_date = #{entryDate},</if>
|
|||
|
|
<if test="studentStatus != null and studentStatus != ''">student_status = #{studentStatus},</if>
|
|||
|
|
|
|||
|
|
<!-- 第二次:351-360行(重复!) -->
|
|||
|
|
<if test="prisonName != null">prison_name = #{prisonName,jdbcType=VARCHAR},</if>
|
|||
|
|
<if test="prisonArea != null">prison_area = #{prisonArea,jdbcType=VARCHAR},</if>
|
|||
|
|
<if test="ethnicity != null">ethnicity = #{ethnicity,jdbcType=VARCHAR},</if>
|
|||
|
|
<if test="educationLevel != null">education_level = #{educationLevel,jdbcType=VARCHAR},</if>
|
|||
|
|
<if test="crimeName != null">crime_name = #{crimeName,jdbcType=VARCHAR},</if>
|
|||
|
|
<if test="sentenceTerm != null">sentence_term = #{sentenceTerm,jdbcType=INTEGER},</if>
|
|||
|
|
<if test="sentenceStartDate != null">sentence_start_date = #{sentenceStartDate,jdbcType=DATE},</if>
|
|||
|
|
<if test="sentenceEndDate != null">sentence_end_date = #{sentenceEndDate,jdbcType=DATE},</if>
|
|||
|
|
<if test="entryDate != null">entry_date = #{entryDate,jdbcType=DATE},</if>
|
|||
|
|
<if test="studentStatus != null and studentStatus != ''">student_status = #{studentStatus},</if>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**结果:** 每个字段都被设置了两次,导致UPDATE执行缓慢!
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### **原因2:数据库索引未创建**
|
|||
|
|
|
|||
|
|
执行 `optimize_import_performance_safe.sql` 添加索引,提升查询速度。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### **原因3:远程数据库延迟**
|
|||
|
|
|
|||
|
|
数据库在远程服务器 `101.35.101.159`,存在网络延迟。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## ✅ **解决方案**
|
|||
|
|
|
|||
|
|
### **修复1:删除重复字段(已完成)**
|
|||
|
|
|
|||
|
|
**文件:** `SysUserMapper.xml`
|
|||
|
|
|
|||
|
|
**修改:** 删除 351-360 行的重复字段设置
|
|||
|
|
|
|||
|
|
**修改前:**
|
|||
|
|
```xml
|
|||
|
|
<if test="studentStatus != null and studentStatus != ''">student_status = #{studentStatus},</if>
|
|||
|
|
<if test="prisonName != null">prison_name = #{prisonName,jdbcType=VARCHAR},</if>
|
|||
|
|
<if test="prisonArea != null">prison_area = #{prisonArea,jdbcType=VARCHAR},</if>
|
|||
|
|
<!-- ...更多重复字段... -->
|
|||
|
|
<if test="studentStatus != null and studentStatus != ''">student_status = #{studentStatus},</if>
|
|||
|
|
update_time = sysdate()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**修改后:**
|
|||
|
|
```xml
|
|||
|
|
<if test="studentStatus != null and studentStatus != ''">student_status = #{studentStatus},</if>
|
|||
|
|
update_time = sysdate()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### **修复2:执行数据库优化SQL**
|
|||
|
|
|
|||
|
|
如果还没执行,请执行:
|
|||
|
|
|
|||
|
|
```sql
|
|||
|
|
-- 文件:log/Sql/optimize_import_performance_safe.sql
|
|||
|
|
|
|||
|
|
-- 主要添加以下索引:
|
|||
|
|
ALTER TABLE sys_user ADD INDEX idx_user_name (user_name);
|
|||
|
|
ALTER TABLE sys_user ADD INDEX idx_nick_name (nick_name);
|
|||
|
|
ALTER TABLE student_class ADD INDEX idx_student_id (student_id);
|
|||
|
|
ALTER TABLE student_class ADD INDEX idx_student_status (student_id, status);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🚀 **部署步骤**
|
|||
|
|
|
|||
|
|
### **步骤1:重新编译**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cd C:\Users\Administrator\Desktop\Project\ry_study-v_03\Study-Vue-redis
|
|||
|
|
mvn clean package -DskipTests
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### **步骤2:重启服务**
|
|||
|
|
|
|||
|
|
停止旧服务,启动新服务
|
|||
|
|
|
|||
|
|
### **步骤3:执行数据库优化(如果还没执行)**
|
|||
|
|
|
|||
|
|
在 Navicat 中连接到数据库 `study`,执行:
|
|||
|
|
```
|
|||
|
|
log/Sql/optimize_import_performance_safe.sql
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### **步骤4:测试导入**
|
|||
|
|
|
|||
|
|
1. 导入100条数据
|
|||
|
|
2. 观察日志,查看UPDATE时间
|
|||
|
|
3. 确认无 "slow sql" 警告
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📊 **性能对比**
|
|||
|
|
|
|||
|
|
### **修复前**
|
|||
|
|
|
|||
|
|
| 指标 | 数值 |
|
|||
|
|
|------|------|
|
|||
|
|
| 每条UPDATE时间 | 1500ms+ |
|
|||
|
|
| 100条总时间 | 2-3分钟 |
|
|||
|
|
| 慢查询警告 | 每条都有 |
|
|||
|
|
| SQL字段 | 重复设置 |
|
|||
|
|
|
|||
|
|
### **修复后(预期)**
|
|||
|
|
|
|||
|
|
| 指标 | 数值 |
|
|||
|
|
|------|------|
|
|||
|
|
| 每条UPDATE时间 | <100ms |
|
|||
|
|
| 100条总时间 | 10-30秒 |
|
|||
|
|
| 慢查询警告 | 无 |
|
|||
|
|
| SQL字段 | 正常 |
|
|||
|
|
|
|||
|
|
**性能提升:15倍以上**
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🧪 **验证方法**
|
|||
|
|
|
|||
|
|
### **测试1:查看SQL执行时间**
|
|||
|
|
|
|||
|
|
导入时查看日志,应该看到:
|
|||
|
|
```
|
|||
|
|
DEBUG c.d.s.m.S.updateUser - ==> Preparing: update sys_user SET ...
|
|||
|
|
DEBUG c.d.s.m.S.updateUser - ==> Parameters: ...
|
|||
|
|
DEBUG c.d.s.m.S.updateUser - <== Updates: 1
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**不应该有** "slow sql" 警告!
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### **测试2:导入速度测试**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 导入100条数据
|
|||
|
|
开始时间: 15:30:00
|
|||
|
|
结束时间: 15:30:15
|
|||
|
|
总耗时: 15秒 ✅
|
|||
|
|
|
|||
|
|
# 修复前需要 2-3 分钟
|
|||
|
|
# 修复后只需 15-30 秒
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### **测试3:验证SQL正确性**
|
|||
|
|
|
|||
|
|
在日志中查看UPDATE SQL,应该是:
|
|||
|
|
|
|||
|
|
```sql
|
|||
|
|
update sys_user
|
|||
|
|
SET dept_id = ?,
|
|||
|
|
user_name = ?,
|
|||
|
|
...
|
|||
|
|
prison_name = ?, -- 只出现一次
|
|||
|
|
prison_area = ?, -- 只出现一次
|
|||
|
|
...
|
|||
|
|
update_time = sysdate()
|
|||
|
|
where user_id = ?
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
每个字段**只出现一次**!
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## ⚠️ **注意事项**
|
|||
|
|
|
|||
|
|
### **1. 必须重新编译**
|
|||
|
|
|
|||
|
|
Mapper 文件修改后必须重新编译,否则不生效!
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
mvn clean package -DskipTests
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### **2. 必须重启服务**
|
|||
|
|
|
|||
|
|
编译完成后必须重启服务,新的Mapper才会加载。
|
|||
|
|
|
|||
|
|
### **3. 数据库索引**
|
|||
|
|
|
|||
|
|
如果没有执行索引SQL,性能提升不明显。
|
|||
|
|
建议执行 `optimize_import_performance_safe.sql`。
|
|||
|
|
|
|||
|
|
### **4. 远程数据库延迟**
|
|||
|
|
|
|||
|
|
即使优化后,远程数据库仍有网络延迟(约50-100ms/条)。
|
|||
|
|
如果需要更快,考虑:
|
|||
|
|
- 使用本地数据库测试
|
|||
|
|
- 批量操作
|
|||
|
|
- 异步处理
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🔍 **问题根源分析**
|
|||
|
|
|
|||
|
|
### **为什么会有重复字段?**
|
|||
|
|
|
|||
|
|
可能的原因:
|
|||
|
|
1. 代码合并时重复添加
|
|||
|
|
2. 不同开发者添加相同字段
|
|||
|
|
3. 复制粘贴时未删除旧代码
|
|||
|
|
|
|||
|
|
### **为什么慢?**
|
|||
|
|
|
|||
|
|
1. **重复字段:** 每个字段设置两次,数据库执行时间翻倍
|
|||
|
|
2. **缺少索引:** 没有索引的查询需要全表扫描
|
|||
|
|
3. **远程延迟:** 网络往返时间增加
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 💡 **优化建议**
|
|||
|
|
|
|||
|
|
### **1. 代码审查**
|
|||
|
|
|
|||
|
|
定期检查Mapper文件,避免重复字段:
|
|||
|
|
```bash
|
|||
|
|
# 查找重复的SET语句
|
|||
|
|
grep -A 50 "<update id" mapper/*.xml | grep "SET"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### **2. 性能监控**
|
|||
|
|
|
|||
|
|
启用慢查询日志,及时发现性能问题:
|
|||
|
|
```yaml
|
|||
|
|
# application.yml
|
|||
|
|
logging:
|
|||
|
|
level:
|
|||
|
|
com.ddnai.system.mapper: DEBUG
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### **3. 批量操作**
|
|||
|
|
|
|||
|
|
对于大量数据导入,使用批量操作:
|
|||
|
|
```java
|
|||
|
|
// 批量INSERT
|
|||
|
|
mapper.batchInsert(userList);
|
|||
|
|
|
|||
|
|
// 批量UPDATE
|
|||
|
|
mapper.batchUpdate(userList);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📋 **检查清单**
|
|||
|
|
|
|||
|
|
- [x] 删除重复字段
|
|||
|
|
- [ ] 重新编译项目
|
|||
|
|
- [ ] 重启服务
|
|||
|
|
- [ ] 执行数据库优化SQL
|
|||
|
|
- [ ] 测试导入100条数据
|
|||
|
|
- [ ] 验证无慢查询警告
|
|||
|
|
- [ ] 确认导入时间正常
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## ✅ **总结**
|
|||
|
|
|
|||
|
|
### **核心问题**
|
|||
|
|
UPDATE SQL 中字段重复设置两次,导致执行缓慢。
|
|||
|
|
|
|||
|
|
### **解决方法**
|
|||
|
|
删除重复的字段设置(351-360行)。
|
|||
|
|
|
|||
|
|
### **预期效果**
|
|||
|
|
- UPDATE时间从 1500ms 降到 <100ms
|
|||
|
|
- 100条导入从 2-3分钟 降到 15-30秒
|
|||
|
|
- **性能提升15倍以上**
|
|||
|
|
|
|||
|
|
### **关键步骤**
|
|||
|
|
1. ✅ 修改Mapper文件
|
|||
|
|
2. ⚠️ 重新编译
|
|||
|
|
3. ⚠️ 重启服务
|
|||
|
|
4. ⚠️ 执行索引SQL(如果还没执行)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
**现在重新编译并重启服务,性能应该有显著提升!** 🚀
|