易君召
易君召
发布于 2026-11-08 / 3 阅读
0

Java 后端性能优化完整方案

优化思路分层:架构层 → JVM 层 → 代码层 → 数据库层 → 中间件层 → 容器 / 操作系统层 → 监控压测验证,优先解决收益最高的瓶颈,不要盲目提前优化。

核心原则:先定位瓶颈,再优化;压测验证优化效果;保留基准指标

一、性能排查手段(先做这个!)

没有定位就没有优化

  1. 压测工具:JMeter、Gatling、k6、JMH(Java 微基准测试,适合方法级)

  2. 链路追踪:SkyWalking / Pinpoint / OpenTelemetry,看慢接口、慢 SQL、外部调用耗时

  3. JVM 诊断

    • 线上:jstat、jmap、jstack、jhat

    • 分析:Arthas(阿里,线上神器)、Async-Profiler(火焰图,CPU / 内存 / 锁)

    • 重点看:GC 频率、GC 停顿、线程阻塞、死锁、OOM、CPU 高占比

  4. 数据库:MySQL 慢查询日志、Explain、Performance Schema

二、架构层面优化(收益最大)

1. 分层缓存,减少下游压力

  • 本地缓存:Caffeine(推荐,优于 Guava Cache),热点小数据,注意缓存失效、容量控制、避免内存溢出

  • 分布式缓存:Redis,缓存 DB 查询结果、配置、token、热点商品;避免缓存穿透 / 击穿 / 雪崩

    • 穿透:布隆过滤器、缓存空值(设置短过期)

    • 击穿:互斥锁、热点数据永不过期

    • 雪崩:过期时间加随机偏移、多级缓存、限流熔断

  • 缓存设计原则:不要缓存大对象、不要强一致性数据、更新策略统一(更新 DB 同时更新 / 删除缓存)

2. 异步化 & 解耦

  • 非核心链路异步:消息队列 RocketMQ / Kafka / RabbitMQ,削峰填谷

    例:下单主流程落库返回,日志、通知、积分、统计丢 MQ 异步消费

  • 线程池规范化:不要直接new Thread()、不要用 Executors 默认线程池(无界队列风险),自定义 ThreadPoolExecutor,核心参数:核心线程、最大线程、队列、拒绝策略

  • CompletableFuture:并行执行独立 IO 任务(多接口并行调用),注意线程池隔离,防止共用线程池被拖垮

3. 限流、熔断、降级(高可用,防止雪崩)

Sentinel / Resilience4j:

  • 限流:QPS 限流、令牌桶 / 漏桶

  • 熔断:下游持续超时直接熔断,快速失败

  • 降级:高峰期关闭非核心功能(推荐、统计、日志)

4. 分库分表 & 读写分离

  • 读多写少:主库写,从库读;Spring 读写分离路由

  • 单表过大(千万级):分表(水平分表按 uid/orderId 哈希);分库隔离业务

  • 注意:跨库 join、分页、排序、分布式事务成本很高,尽量业务规避

5. 服务拆分 & 资源隔离

  • 核心业务、非核心业务服务 / 线程池隔离,互不抢占资源

  • 大流量接口独立部署,资源隔离

三、数据库优化(绝大多数后端瓶颈在这里)

MySQL 优化

  1. 索引优化

    • 遵循最左前缀;避免失效:like '%xxx'、函数、隐式类型转换、or、not in

    • 不要过度建索引,索引会拖慢写入

    • 联合索引把等值放前面,范围放最后

    • 用explain看 type、key、rows、Extra(Using filesort、Using temporary 要重点优化)

  2. SQL 写法

    • 禁止select *,只查需要字段;减少回表

    • 大分页不要offset 100000,使用主键分页:where id > ? limit ?

    • 避免大事务:事务越小越好,锁持有时间越短越好

    • 批量操作:batch insert/update,控制批次大小(500~1000,太大锁表、binlog 暴涨)

  3. 表结构

    • 合适字段类型:用 int 代替 varchar 存状态,日期 datetime 代替 varchar;字段尽量 NOT NULL

    • 大字段(text、blob)拆到独立附表,避免查询拖慢

  4. 数据库配置:连接池、buffer pool、redo log、binlog 策略

  5. 连接池:HikariCP(首选),合理设置最大连接数,连接数不是越大越好

ORM 避坑(MyBatis / JPA)

  • 禁止循环里单条查库(N+1 问题!),批量查询、IN 查询、关联查询

  • MyBatis:懒加载慎用;resultMap 复用;避免动态 SQL 写烂索引

  • JPA/Hibernate:不要默认 fetch EAGER,慎用级联

四、JVM 调优

现代 JDK(JDK17+,SpringBoot3 推荐)默认 G1 已经很好,不要上来乱改参数,先看 GC 日志

  1. GC 选择

    • JDK8:G1;高延迟低吞吐:ZGC(JDK11 + 稳定);超高吞吐:Parallel GC

    • 生产优先 ZGC:低停顿(亚毫秒级),适合接口响应敏感业务

  2. 基础参数要点

    • Xmx、Xms 设置成一样,减少堆扩容

    • 堆不要无限大,受宿主机内存限制,预留 OS、容器、缓存内存

    • 开启 GC 日志、使用 Async-Profiler 抓 GC 停顿

  3. 内存泄漏常见点

    • 静态集合长期持有对象、线程池无限堆积任务、未关闭流、ThreadLocal 忘记 remove(极易内存泄漏!)

    • 大对象频繁创建,进入老年代,触发 FullGC

  4. 小优化

    • 对象池:频繁创建销毁的重对象(Netty Buffer、连接),普通 POJO 不要池化

    • 优先栈上分配、逃逸分析开启

五、Java 代码层面优化(单接口微观优化)

注意:代码优化收益通常低于数据库 / 缓存,不要过早优化可读性

  1. 集合

    • 遍历:ArrayList 随机访问快;LinkedList 随机访问极慢

    • HashMap:预估初始容量,减少 rehash;new HashMap(128)而不是默认 16

    • 线程安全:优先 Caffeine、ConcurrentHashMap;放弃 Hashtable、Collections.synchronizedMap

    • 遍历集合不要在 for 里 remove(用迭代器 remove)

  2. 字符串

    • 循环拼接:StringBuilder,不要+(编译器优化只在简单场景生效)

    • 大文本不要用 String,考虑流式处理

  3. IO 资源

    • 所有流、连接、Socket:try-with-resources 自动关闭,防止句柄泄漏

  4. 循环

    • 循环内避免 new 对象、避免重复计算(提前提取常量)

    • 避免深层嵌套循环

  5. 序列化

    • JSON:Jackson > Fastjson2;序列化大对象非常耗 CPU;减少传输字段

    • 内部 RPC:Protobuf/Hessian,比 JSON 体积更小、更快

  6. 日期、正则

    • 禁止SimpleDateFormat(非线程安全),用java.time(JDK8+ LocalDateTime)

    • 正则预编译 Pattern,不要每次 new

  7. 锁优化

    • 缩小锁粒度;优先无锁、CAS、ThreadLocal、读写锁 ReentrantReadWriteLock

    • 避免锁竞争:分段锁;不要在锁内执行 IO/DB 调用(锁持有太久)

    • synchronized 偏向锁、轻量级锁了解,不要盲目换成 Lock

  8. Stream

    • 简单遍历 for 循环性能高于 Stream;并行流parallelStream()慎用!默认共用 ForkJoinPool,容易挤占全局线程,IO 密集不适合并行流

六、RPC & 外部调用优化

  • Feign/Dubbo:超时时间、重试策略合理配置;重试只允许幂等接口

  • 外部 HTTP 调用:连接池(OkHttp、Apache HttpClient),复用连接,减少 TCP 握手

  • 串行独立 IO 改成并行(CompletableFuture.allOf),注意超时控制,防止一个慢调用拖整体

  • 批量接口:优先批量查询代替多次单条调用

七、容器 & 操作系统层面(K8s/Docker)

  1. Docker:JDK 感知容器内存(JDK10 + 默认支持,JDK8 需要参数),避免 OOM kill

  2. Linux:文件句柄数 ulimit、TCP 参数(tcp_tw_reuse、连接队列)

  3. CPU 绑定、调度策略:高吞吐业务避免频繁上下文切换

八、常用避坑清单(高频踩坑)

✅ 禁止:循环查库 N+1 ✅ 禁止:无界队列线程池 ✅ 禁止:ThreadLocal 不清理 ✅ 禁止:大事务、长事务 ✅ 禁止:缓存大对象、缓存无限增长 ✅ 禁止:接口里面同步调用一堆第三方串行调用 ✅ 禁止:分页 offset 超大 ✅ 禁止:连接池设置过大

九、优化落地顺序(推荐)

  1. 压测 + 链路追踪,定位瓶颈(慢 SQL / 外部调用 / GC / 锁)

  2. 优先:加缓存、异步化、优化 SQL 和索引(收益最高)

  3. 然后:限流熔断、读写分离分表

  4. 再做:JVM 调优、线程池规范

  5. 最后:微观代码优化

十、示例:JMH 做接口内方法基准测试

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public class StringBench {
    @Benchmark
    public String testBuilder() {
        StringBuilder sb = new StringBuilder();
        for(int i=0;i<100;i++) sb.append(i);
        return sb.toString();
    }
}

本文原创作者:易君召,详见:https://www.yijunzhao.cn/authors/yijunzhao,转载请注明出处。

原文链接 https://www.yijunzhao.cn/archives/java-backend-performance-optimization-guide

欢迎访问 小易撩挨踢

https://www.yijunzhao.cn/