performance-doctor

1. 定位 -> 接口层面(响应时间) / SQL层面(慢查询) / 缓存层面(命中率) 2. 分析 -> SQL 日志 / Arthas JVM 诊断 / 日志分析 3. 优化 -> 索引 / 缓存 / 批量处理 4. 验证 -> 对比优化前后指标

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "performance-doctor" with this command: npx skills add xu-cell/ai-engineering-init/xu-cell-ai-engineering-init-performance-doctor

后端性能优化指南

诊断流程

  1. 定位 -> 接口层面(响应时间) / SQL层面(慢查询) / 缓存层面(命中率)
  2. 分析 -> SQL 日志 / Arthas JVM 诊断 / 日志分析
  3. 优化 -> 索引 / 缓存 / 批量处理
  4. 验证 -> 对比优化前后指标

性能指标

指标 良好 需优化 工具

接口响应 < 200ms

500ms p6spy/SkyWalking

数据库查询 < 50ms

200ms SQL 日志

内存使用 < 70%

85% Arthas/JVM 监控

  1. MyBatis-Plus 查询优化

查询构建规范

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers;

private LambdaQueryWrapper<XxxEntity> buildQueryWrapper(XxxQueryDTO query) { LambdaQueryWrapper<XxxEntity> lqw = Wrappers.lambdaQuery(); lqw.eq(query.getDeptId() != null, XxxEntity::getDeptId, query.getDeptId()); lqw.like(StrUtil.isNotBlank(query.getKeyword()), XxxEntity::getName, query.getKeyword()); lqw.between(query.getStartTime() != null && query.getEndTime() != null, XxxEntity::getCreateTime, query.getStartTime(), query.getEndTime()); return lqw; }

更新构建规范

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;

// 精确更新 baseMapper.update(null, new LambdaUpdateWrapper<XxxEntity>() .set(XxxEntity::getStatus, status) .eq(XxxEntity::getId, id));

// 条件更新(非null才设置) baseMapper.update(null, new LambdaUpdateWrapper<XxxEntity>() .set(ObjectUtil.isNotNull(entity.getName()), XxxEntity::getName, entity.getName()) .set(XxxEntity::getPhone, entity.getPhone()) .eq(XxxEntity::getId, entity.getId()));

批量更新

import com.baomidou.mybatisplus.extension.toolkit.Db;

// 推荐:Db 工具类批量更新 Db.updateBatchById(entityList);

// 避免:循环单个更新(N次数据库访问)

N+1 查询修复

// ❌ 错误:循环查询 for (Order order : orders) { User user = userMapper.selectById(order.getUserId()); }

// ✅ 正确:批量查询 + Map 映射 List<Long> userIds = orders.stream().map(Order::getUserId).distinct().toList(); Map<Long, User> userMap = userMapper.selectBatchIds(userIds).stream() .collect(Collectors.toMap(User::getId, Function.identity())); for (Order order : orders) { User user = userMap.get(order.getUserId()); }

  1. 缓存优化

Redisson 缓存操作

import org.redisson.api.RedissonClient; import org.redisson.api.RBucket;

// 设置缓存 RBucket<UserVo> bucket = redissonClient.getBucket("user:" + id); bucket.set(userVo, Duration.ofMinutes(30));

// 获取缓存 UserVo cached = redissonClient.<UserVo>getBucket("user:" + id).get();

// 删除缓存 redissonClient.getBucket("user:" + id).delete();

Spring Cache 注解

@Cacheable(value = "user", key = "#id") public UserVo getById(Long id) { ... }

@CacheEvict(value = "user", key = "#bo.id") public int update(UserBo bo) { ... }

@Cacheable 禁止返回不可变集合(List.of() 、Set.of() ),Redis 反序列化会失败。必须用 new ArrayList<>(List.of(...)) 。

分布式锁

import org.redisson.api.RLock;

// 注入 RedissonClient RLock lock = redissonClient.getLock("lock:order:" + orderId); try { if (lock.tryLock(3, 10, TimeUnit.SECONDS)) { try { // 业务逻辑 } finally { if (lock.isLocked() && lock.isHeldByCurrentThread()) { lock.unlock(); } } } else { throw new 你的异常类; } } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new 你的异常类; }

限流

import org.redisson.api.RRateLimiter; import org.redisson.api.RateType; import org.redisson.api.RateIntervalUnit;

RRateLimiter limiter = redissonClient.getRateLimiter("api:rate:" + userId); limiter.trySetRate(RateType.OVERALL, 100, 60, RateIntervalUnit.SECONDS); if (!limiter.tryAcquire()) { throw new 你的异常类; }

本地缓存(Caffeine)

import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine;

private final Cache<Long, UserVo> localCache = Caffeine.newBuilder() .maximumSize(1000) .expireAfterWrite(5, TimeUnit.MINUTES) .build();

public UserVo getUser(Long id) { return localCache.get(id, key -> { RBucket<UserVo> bucket = redissonClient.getBucket("user:" + id); UserVo cached = bucket.get(); return cached != null ? cached : baseMapper.selectById(id); }); }

  1. 索引优化

-- 为高频查询字段添加索引 CREATE INDEX idx_status ON xxx_table(status);

-- 复合索引:遵循最左前缀原则 CREATE INDEX idx_status_create_time ON xxx_table(status, create_time);

-- 使用 EXPLAIN 分析查询 EXPLAIN SELECT * FROM xxx_table WHERE status = 1 AND create_time > '2026-01-01';

  1. 批量操作优化

// 推荐:分批处理(每批500条) public void batchInsert(List<XxxEntity> list) { int batchSize = 500; for (int i = 0; i < list.size(); i += batchSize) { int end = Math.min(i + batchSize, list.size()); baseMapper.insertBatch(list.subList(i, end)); } }

// 批量更新同理 @Transactional(rollbackFor = Exception.class) public void batchUpdate(List<XxxDTO> list) { int batchSize = 500; for (int i = 0; i < list.size(); i += batchSize) { int end = Math.min(i + batchSize, list.size()); List<XxxEntity> batch = list.subList(i, end).stream() .map(dto -> BeanUtil.copyProperties(dto, XxxEntity.class)) .toList(); Db.updateBatchById(batch); } }

  1. 性能日志分析

SQL 监控

开发环境 p6spy 默认开启:spring.datasource.dynamic.p6spy: true

日志格式:

2026-01-08 22:12:10 [req-123] [...] INFO p6spy - Consume Time:245 ms Execute SQL:SELECT * FROM user WHERE status = 1

分析命令

慢 SQL(>200ms)

grep "Consume Time" ./logs/console.log | grep -E "Consume Time:[2-9][0-9]{2,}|[0-9]{4,}"

N+1 检测(同一 SQL 重复多次)

grep "Execute SQL" ./logs/console.log | sed 's/WHERE.*/WHERE .../' | sort | uniq -c | sort -rn | head -20

错误日志

grep "ERROR" ./logs/console.log | tail -50

AI 主动读日志的触发条件

场景 关键词 分析重点

接口慢 "接口慢"、"超时" SQL 执行时间、N+1 查询

SQL 慢 "SQL慢"、"查询慢" p6spy Consume Time

内存/CPU "内存高"、"卡顿" OOM、GC、线程池满

频繁报错 "一直报错" ERROR 日志、异常频率

常见问题速查

问题 原因 方案

接口响应慢 SQL 无索引 添加索引,EXPLAIN 分析

接口响应慢 N+1 查询 批量查询 + Map 映射

接口响应慢 未缓存 Redisson 或 @Cacheable

分页查询慢 深分页 游标分页或限制页码

批量操作超时 数据量太大 分批500条处理

内存溢出 大数据量一次加载 分批/流式处理

监控工具

工具 用途 使用方式

p6spy SQL 监控、执行时间 开发环境默认开启

Arthas JVM 诊断、火焰图 java -jar arthas-boot.jar

SkyWalking 分布式链路追踪 需单独部署

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

loki-log-query

No summary provided by upstream source.

Repository SourceNeeds Review
General

scheduled-jobs

No summary provided by upstream source.

Repository SourceNeeds Review
General

brainstorm

No summary provided by upstream source.

Repository SourceNeeds Review
General

openspec-ff-change

No summary provided by upstream source.

Repository SourceNeeds Review