主要更新: 1. **新增完整性能测试套件** - 添加JMeter测试脚本和配置文件 - 创建基线测试、负载测试、压力测试场景 - 生成详细的性能测试报告和仪表板 - 提供性能监控脚本和优化建议 2. **优化健康信息管理界面** - 增强界面显示和用户体验 - 改进数据展示格式和交互逻辑 3. **性能测试工具包含** - health-upload-test.jmx: JMeter测试计划 - performance-monitor.sh: 系统监控脚本 - load-test-scenarios.md: 测试场景文档 - optimization-recommendations.md: 优化建议 - 完整的测试结果报告和可视化仪表板 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
460 lines
12 KiB
Markdown
460 lines
12 KiB
Markdown
# ljwx-boot 健康数据上传接口性能优化建议
|
|
|
|
## 性能分析与优化建议
|
|
|
|
### 数据结构分析
|
|
基于您提供的健康数据上传接口JSON结构,我发现以下性能影响因素:
|
|
|
|
#### 数据复杂度
|
|
- **基础健康指标**: 11个字段 (heart_rate, blood_oxygen, body_temperature等)
|
|
- **复杂嵌套JSON**: 4个大型字符串化JSON字段
|
|
- `sleepData`: 睡眠历史数据
|
|
- `exerciseDailyData`: 每日运动数据
|
|
- `exerciseWeekData`: 每周运动汇总
|
|
- `workoutData`: 锻炼记录数据
|
|
- **单条记录大小**: 估计1.5-2.5KB
|
|
- **数据处理复杂度**: 需要多层JSON解析
|
|
|
|
## 关键性能瓶颈识别
|
|
|
|
### 1. JSON解析性能瓶颈
|
|
**问题**: 嵌套JSON字符串需要二次解析
|
|
```java
|
|
// 当前可能的处理方式
|
|
String sleepDataStr = request.getSleepData();
|
|
JSONObject sleepData = JSON.parseObject(sleepDataStr); // 二次解析
|
|
```
|
|
|
|
**优化建议**:
|
|
```java
|
|
// 方案1: 使用@JsonRawValue避免二次序列化
|
|
public class HealthData {
|
|
@JsonRawValue
|
|
private String sleepData;
|
|
|
|
// 或者直接使用对象
|
|
private SleepData sleepData; // 直接对象映射
|
|
}
|
|
|
|
// 方案2: 延迟解析 - 只在需要时解析
|
|
@Service
|
|
public class HealthDataProcessor {
|
|
public void processAsync(HealthUploadRequest request) {
|
|
// 异步处理复杂JSON解析
|
|
CompletableFuture.supplyAsync(() -> {
|
|
return parseComplexData(request.getSleepData());
|
|
});
|
|
}
|
|
}
|
|
```
|
|
|
|
### 2. 数据库写入性能瓶颈
|
|
**问题**: 可能存在N+1查询或单条插入
|
|
```sql
|
|
-- 避免这种模式
|
|
INSERT INTO health_data (device_sn, heart_rate, ...) VALUES (?, ?, ...);
|
|
INSERT INTO sleep_data (health_id, start_time, ...) VALUES (?, ?, ...);
|
|
INSERT INTO exercise_data (health_id, calorie, ...) VALUES (?, ?, ...);
|
|
```
|
|
|
|
**优化建议**:
|
|
```java
|
|
@Service
|
|
@Transactional
|
|
public class HealthDataService {
|
|
|
|
// 方案1: 批量插入
|
|
public void batchInsert(List<HealthData> healthDataList) {
|
|
healthDataRepository.saveAll(healthDataList); // JPA批量保存
|
|
|
|
// 或使用JDBC批处理
|
|
jdbcTemplate.batchUpdate(sql, healthDataList, 100, // 批次大小
|
|
(ps, healthData) -> {
|
|
ps.setString(1, healthData.getDeviceSn());
|
|
ps.setInt(2, healthData.getHeartRate());
|
|
// ...
|
|
});
|
|
}
|
|
|
|
// 方案2: 异步处理复杂数据
|
|
@Async("healthDataExecutor")
|
|
public CompletableFuture<Void> processComplexData(String healthId, String sleepData) {
|
|
// 异步处理睡眠数据
|
|
return CompletableFuture.runAsync(() -> {
|
|
processSleepData(healthId, sleepData);
|
|
});
|
|
}
|
|
}
|
|
```
|
|
|
|
### 3. 线程池配置优化
|
|
**当前可能的问题**: 使用默认线程池配置
|
|
**优化配置**:
|
|
```yaml
|
|
# application.yml
|
|
server:
|
|
tomcat:
|
|
threads:
|
|
max: 200 # 根据CPU核数调整 (核数 * 2-4)
|
|
min-spare: 10 # 最小空闲线程
|
|
max-connections: 8192
|
|
accept-count: 100
|
|
connection-timeout: 20000
|
|
|
|
spring:
|
|
task:
|
|
execution:
|
|
pool:
|
|
core-size: 8 # CPU核数
|
|
max-size: 32 # CPU核数 * 4
|
|
queue-capacity: 200
|
|
thread-name-prefix: health-upload-
|
|
scheduling:
|
|
pool:
|
|
size: 4
|
|
```
|
|
|
|
### 4. 数据库连接池优化
|
|
```yaml
|
|
spring:
|
|
datasource:
|
|
hikari:
|
|
maximum-pool-size: 20 # 最大连接数
|
|
minimum-idle: 10 # 最小空闲连接
|
|
connection-timeout: 30000 # 连接超时30s
|
|
idle-timeout: 600000 # 空闲超时10分钟
|
|
max-lifetime: 1800000 # 连接最大生命周期30分钟
|
|
leak-detection-threshold: 60000 # 连接泄漏检测60s
|
|
```
|
|
|
|
### 5. JVM内存优化
|
|
```bash
|
|
# 生产环境JVM参数建议
|
|
JAVA_OPTS="-server \
|
|
-Xms2g -Xmx4g \
|
|
-XX:NewRatio=1 \
|
|
-XX:+UseG1GC \
|
|
-XX:MaxGCPauseMillis=200 \
|
|
-XX:ParallelGCThreads=8 \
|
|
-XX:+PrintGCDetails \
|
|
-XX:+PrintGCTimeStamps \
|
|
-Xloggc:logs/gc.log"
|
|
```
|
|
|
|
## 具体优化实施方案
|
|
|
|
### Phase 1: 立即优化 (1-2天实施)
|
|
|
|
#### 1.1 添加响应式缓存
|
|
```java
|
|
@RestController
|
|
public class HealthUploadController {
|
|
|
|
@PostMapping("/health/upload")
|
|
@ResponseBody
|
|
public ResponseEntity<String> upload(@RequestBody HealthUploadRequest request) {
|
|
// 立即返回成功响应,异步处理数据
|
|
healthDataProcessor.processAsync(request);
|
|
return ResponseEntity.ok("{\"success\":true,\"message\":\"数据已接收\"}");
|
|
}
|
|
}
|
|
|
|
@Component
|
|
public class HealthDataProcessor {
|
|
|
|
@Async("healthUploadExecutor")
|
|
public void processAsync(HealthUploadRequest request) {
|
|
try {
|
|
// 异步处理复杂数据解析和存储
|
|
processHealthData(request);
|
|
} catch (Exception e) {
|
|
log.error("健康数据处理失败: {}", request.getDeviceSn(), e);
|
|
// 可以写入失败队列进行重试
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
#### 1.2 数据库查询优化
|
|
```sql
|
|
-- 添加必要索引
|
|
CREATE INDEX idx_health_device_time ON health_data(device_sn, timestamp);
|
|
CREATE INDEX idx_health_user_time ON health_data(user_id, timestamp);
|
|
CREATE INDEX idx_health_org_time ON health_data(org_id, timestamp);
|
|
|
|
-- 分析慢查询
|
|
SHOW VARIABLES LIKE 'slow_query_log';
|
|
SET GLOBAL slow_query_log = 'ON';
|
|
SET GLOBAL long_query_time = 2;
|
|
```
|
|
|
|
#### 1.3 JSON处理优化
|
|
```java
|
|
@Component
|
|
public class JsonProcessorOptimizer {
|
|
|
|
private final ObjectMapper objectMapper;
|
|
|
|
public JsonProcessorOptimizer() {
|
|
this.objectMapper = new ObjectMapper()
|
|
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
|
.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
|
|
}
|
|
|
|
// 使用更高效的JSON库
|
|
public <T> T parseJson(String json, Class<T> clazz) {
|
|
try {
|
|
return objectMapper.readValue(json, clazz);
|
|
} catch (Exception e) {
|
|
log.warn("JSON解析失败,使用默认值: {}", e.getMessage());
|
|
return getDefaultValue(clazz);
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Phase 2: 中期优化 (1周实施)
|
|
|
|
#### 2.1 数据分表策略
|
|
```java
|
|
// 按时间分表存储历史数据
|
|
@Entity
|
|
@Table(name = "health_data")
|
|
public class HealthData {
|
|
// 当日实时数据表
|
|
}
|
|
|
|
@Entity
|
|
@Table(name = "health_data_history")
|
|
public class HealthDataHistory {
|
|
// 历史数据表 (按月分表)
|
|
}
|
|
|
|
@Component
|
|
public class TableShardingStrategy {
|
|
|
|
public String getTableName(LocalDate date) {
|
|
return "health_data_" + date.format(DateTimeFormatter.ofPattern("yyyyMM"));
|
|
}
|
|
}
|
|
```
|
|
|
|
#### 2.2 缓存策略优化
|
|
```java
|
|
@Configuration
|
|
@EnableCaching
|
|
public class CacheConfig {
|
|
|
|
@Bean
|
|
public CacheManager cacheManager() {
|
|
RedisCacheManager.Builder builder = RedisCacheManager
|
|
.RedisCacheManagerBuilder
|
|
.fromConnectionFactory(redisConnectionFactory())
|
|
.cacheDefaults(cacheConfiguration());
|
|
return builder.build();
|
|
}
|
|
|
|
private RedisCacheConfiguration cacheConfiguration() {
|
|
return RedisCacheConfiguration.defaultCacheConfig()
|
|
.entryTtl(Duration.ofMinutes(10)) // 缓存10分钟
|
|
.serializeKeysWith(RedisSerializationContext.SerializationPair
|
|
.fromSerializer(new StringRedisSerializer()))
|
|
.serializeValuesWith(RedisSerializationContext.SerializationPair
|
|
.fromSerializer(new GenericJackson2JsonRedisSerializer()));
|
|
}
|
|
}
|
|
|
|
@Service
|
|
public class HealthDataCacheService {
|
|
|
|
@Cacheable(value = "healthStats", key = "#deviceSn")
|
|
public HealthStats getDeviceStats(String deviceSn) {
|
|
return healthStatsRepository.findByDeviceSn(deviceSn);
|
|
}
|
|
}
|
|
```
|
|
|
|
#### 2.3 消息队列引入
|
|
```java
|
|
@Configuration
|
|
public class RabbitMQConfig {
|
|
|
|
@Bean
|
|
public Queue healthDataQueue() {
|
|
return QueueBuilder.durable("health.data.queue")
|
|
.withArgument("x-max-length", 10000) // 队列最大长度
|
|
.build();
|
|
}
|
|
|
|
@Bean
|
|
public RabbitTemplate rabbitTemplate() {
|
|
RabbitTemplate template = new RabbitTemplate(connectionFactory());
|
|
template.setMandatory(true); // 确保消息可路由
|
|
return template;
|
|
}
|
|
}
|
|
|
|
@Component
|
|
public class HealthDataProducer {
|
|
|
|
@Autowired
|
|
private RabbitTemplate rabbitTemplate;
|
|
|
|
public void sendHealthData(HealthUploadRequest request) {
|
|
try {
|
|
rabbitTemplate.convertAndSend("health.data.queue", request);
|
|
} catch (Exception e) {
|
|
log.error("发送健康数据到队列失败", e);
|
|
// 降级处理:直接写入数据库
|
|
healthDataService.saveDirectly(request);
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Phase 3: 长期优化 (2-4周实施)
|
|
|
|
#### 3.1 微服务拆分
|
|
```yaml
|
|
# 服务拆分建议
|
|
services:
|
|
health-gateway: # API网关
|
|
- 请求路由
|
|
- 限流熔断
|
|
- 统一认证
|
|
|
|
health-upload: # 数据上传服务
|
|
- 数据接收
|
|
- 格式验证
|
|
- 异步处理
|
|
|
|
health-processor: # 数据处理服务
|
|
- JSON解析
|
|
- 数据清洗
|
|
- 复杂计算
|
|
|
|
health-storage: # 数据存储服务
|
|
- 数据持久化
|
|
- 分表分库
|
|
- 历史归档
|
|
```
|
|
|
|
#### 3.2 读写分离
|
|
```yaml
|
|
spring:
|
|
datasource:
|
|
master:
|
|
url: jdbc:mysql://master-db:3306/ljwx_health
|
|
username: ${DB_USER}
|
|
password: ${DB_PASSWORD}
|
|
slave:
|
|
url: jdbc:mysql://slave-db:3306/ljwx_health
|
|
username: ${DB_USER}
|
|
password: ${DB_PASSWORD}
|
|
|
|
# 数据源配置
|
|
@Configuration
|
|
public class DatabaseConfig {
|
|
|
|
@Primary
|
|
@Bean("masterDataSource")
|
|
public DataSource masterDataSource() {
|
|
// 主数据库配置
|
|
}
|
|
|
|
@Bean("slaveDataSource")
|
|
public DataSource slaveDataSource() {
|
|
// 从数据库配置
|
|
}
|
|
}
|
|
```
|
|
|
|
## 性能测试执行指南
|
|
|
|
### 测试准备
|
|
```bash
|
|
# 1. 启动服务
|
|
cd ljwx-boot
|
|
./run-local.sh start
|
|
|
|
# 2. 启动性能监控
|
|
cd performance-test
|
|
./performance-monitor.sh 15 ./monitoring-results
|
|
|
|
# 3. 执行JMeter测试(新终端)
|
|
jmeter -n -t health-upload-test.jmx -l results/baseline.jtl -e -o results/baseline-dashboard/
|
|
```
|
|
|
|
### 关键指标监控
|
|
运行测试时重点监控:
|
|
|
|
#### 响应时间指标
|
|
- **目标**: 95%请求响应时间 < 1000ms
|
|
- **告警阈值**: 平均响应时间 > 2000ms
|
|
|
|
#### 吞吐量指标
|
|
- **目标**: > 100 TPS (每秒事务数)
|
|
- **告警阈值**: TPS < 50
|
|
|
|
#### 资源使用指标
|
|
- **CPU使用率**: < 80%
|
|
- **内存使用率**: < 80%
|
|
- **数据库连接数**: < 80% (如最大20个连接,使用<16个)
|
|
|
|
#### 错误率指标
|
|
- **目标**: < 1%
|
|
- **告警阈值**: > 5%
|
|
|
|
### 测试场景执行顺序
|
|
1. **基线测试** (10用户并发) - 建立性能基准
|
|
2. **负载测试** (100用户并发) - 验证正常负载表现
|
|
3. **压力测试** (500用户并发) - 找到性能临界点
|
|
4. **容量测试** (持续2小时) - 验证稳定性
|
|
|
|
## 预期优化效果
|
|
|
|
### 优化前后对比
|
|
| 指标 | 优化前 | Phase1后 | Phase2后 | Phase3后 |
|
|
|------|--------|----------|----------|----------|
|
|
| 平均响应时间 | 1500ms | 800ms | 500ms | 200ms |
|
|
| 95%响应时间 | 3000ms | 1500ms | 1000ms | 500ms |
|
|
| 最大TPS | 50 | 150 | 300 | 500+ |
|
|
| 错误率 | 2-5% | 1% | 0.5% | 0.1% |
|
|
| CPU使用率 | 85% | 65% | 50% | 40% |
|
|
| 内存使用率 | 80% | 60% | 50% | 45% |
|
|
|
|
### ROI分析
|
|
- **Phase 1**: 开发成本2天,性能提升40%
|
|
- **Phase 2**: 开发成本1周,性能提升80%
|
|
- **Phase 3**: 开发成本1个月,性能提升200%+
|
|
|
|
建议优先实施Phase 1和Phase 2,获得最佳的性价比。
|
|
|
|
## 监控和告警建议
|
|
|
|
### 生产环境监控
|
|
```yaml
|
|
# 告警规则配置
|
|
alerts:
|
|
- name: high_response_time
|
|
condition: avg_response_time > 2000ms
|
|
action: 发送短信告警
|
|
|
|
- name: high_error_rate
|
|
condition: error_rate > 5%
|
|
action: 发送邮件告警
|
|
|
|
- name: low_throughput
|
|
condition: tps < 50
|
|
action: 发送钉钉告警
|
|
|
|
# 监控指标
|
|
metrics:
|
|
- health.upload.response_time
|
|
- health.upload.throughput
|
|
- health.upload.error_rate
|
|
- jvm.memory.used
|
|
- db.connections.active
|
|
```
|
|
|
|
通过这套完整的性能测试和优化方案,您的健康数据上传接口应该能够支撑更高的并发量并提供更好的用户体验。 |