笔者曾经写过一篇文章,介绍 ShardingSphere 在具体代码细节上的优化案例,但文章中没有介绍如何发现代码优化点。本文将结合之前笔者在 ShardingSphere 相关性能问题排查、优化经验,简要地介绍 ShardingSphere 性能问题排查、优化应如何入手。由于 ShardingSphere 本身也是 Java 应用,本文的经验同样也适用于与 ShardingSphere 无关的 Java 应用。
文章目录
确实需要进行性能优化吗?
Premature optimization is the root of all evil.
– Donald Knuth
进行性能优化之前,可以先想想为什么要优化?
笔者曾经认为性能工程可分为两种情况:
- 应用性能出现异常,与预期运行状况有偏差,需要确认、排查、解决性能问题,使性能达到预期状况;
- 应用工作状态良好,本身没有明显的性能问题,吞吐量已经达到瓶颈,在应用以外的条件不改变的前提下,进一步优化以提升峰值吞吐量。
后来,在多次参与性能相关工作后,发现性能相关的情况其实比较复杂:
- 主观上或通过简单的测试认为性能存在问题,无意中做了负优化却没有发现;
- 性能测试方法存在问题,或问题在不同环境的影响存在差异,导致测不出性能问题;
- 有些应用在表现上看似没有明显问题,但实际上问题存在已久,以至于让人以为应用的表现本该如此;
- 没能关注到性能问题真正所在。例如,系统性能问题出在应用本身,却在数据库层面做了大量工作;
- 性能测试方法与实际场景偏差较大,即使测试指标表现良好,但实际性能表现与测试结果偏差较大;也可能性能测试结果不如预期,但实际上系统运行状况良好且满足业务需求;
- ……
以 ShardingSphere 为例,就曾经出现过:
- TableMetaData 自 4.0.0-RC2 版本使用了 Collections.synchronizedMap 方法,直到 5.0.0 版本才发现其对并发性能的影响并得以解决;
- 通过 ConcurrentHashMap 缓存对象以减少创建的开销,却无意间踩到了 Java 8 下 computeIfAbsent 相关并发性能问题,但可能没有合适的并发性能测试,以至于当时未能发现问题;
- ShardingSphere-Proxy 主流程代码中引入了 synchronized 方法,在某 x86 环境下使用简单场景测试工具没有发现明显的性能下降,在某 aarch64 环境下高并发模拟业务场景测试发现性能下降 40%;
- ……
笔者曾经接触过一个系统,在运行期间向 stdout 和文件同时打着大量日志,完整输出每个 HTTP 请求详情,但是实际业务运转正常,非功能性方面用户体验良好,系统成本上也没有明显的异议。这种情况下,投入成本给系统做性能优化,好像并没有太大的必要。
平时也有同学咨询现有的 ShardingSphere 性能测试结果。在不同的场景、环境、方法下测试,ShardingSphere 的性能表现差异可能会很大,参考意义并不大。
性能本身存在较大的主观性,脱离实际场景是无法讨论的。 充分评估业务的非功能性需求、收集真实用户的反馈信息、考虑投入的成本与可能的收益、进行客观准确的性能测试,最后再判断当前是否存在性能问题,或需要对性能进一步优化。
如何选择观察工具?
选择合适的工具观察系统、应用性能,在性能优化的过程中会起到很大的作用。
大多数生产、压测等环境一般都部署了可观察性平台,用于监测系统的各项指标、跟踪全链路等,在必要时进行告警行为。这类可观察性具备规模大、常态化、通用性高等特点,同样,这些特点也注定了可观察性工具在指标的粒度、实时性、针对性等方面存在一定的局限。
直接登录到被优化对象所在的环境,通过各种命令行工具直接查看系统状况,可能会比操作图形化的可观察性平台更繁琐,但这种方式更灵活、更直接,能够观察到更多的细节。
另外,对于特定领域,可能会有更有针对性的观察工具。例如对 JVM 进程进行采集,Linux 下有 perf 工具能够采集系统事件、堆栈等信息,采集的信息可生成火焰图,但 perf 等工具在 Java 代码堆栈的采集有一定局限性;使用 async-profiler 等工具能够采集到堆内存分配、Object Monitor 等信息,JFR 格式的采集结果能够直接导入 Java 性能分析工具,分析 Java 应用性能更方便。
举个例子,以下是一台多核服务器在应用性能测试期间,在监控面板中,观察到的 CPU 使用率可能长下面这个样子:

直接登录到服务器环境,使用 htop 观察到的 CPU 使用率却可能是下面这个样子(上下图没有直接关系):

虽然在可观察性界面中也能够配置具体到每个 CPU 核心的图表,但大部分的面板默认只展示了 CPU 的整体使用率,尤其是在 CPU 核心数较多的环境下,在面板中展示过于详细的信息可能会让界面变得复杂。
以上这种情况,很容易让调优人员忽略了某些 CPU 核心上的异常表现,增加了性能问题的定位难度。
先尽可能排除应用之外的因素
性能问题可能很简单,可能很复杂,也可能让人出乎意料。
例如在上一节案例中展示的诡异的 CPU 使用率,与网卡队列、软中断有关,影响了网络收发性能,导致应用程序无法充分利用整机 CPU 资源。
曾经也遇到过有用户在 aarch64 环境部署 ShardingSphere-Proxy 后,发现 Proxy 性能与预期结果存在数量级上的差异,通过 jstat -gc 发现 JVM 频繁 STW (Stop-The-World),更换了一个 JVM 后问题解决。
影响性能的因素难以完全枚举,在进一步关注应用本身的性能表现之前,应该先检查性能是否受其他因素的影响。
Java 应用相关性能问题排查
如果已经尽可能排除应用之外影响性能的因素,下一步就是对应用本身的情况进行观察。
Apache ShardingSphere 提供的 ShardingSphere-Proxy 镜像使用 JDK 而不是尺寸更小 JRE 作为基础镜像,理由之一是 JDK 包含了一些观察 JVM 进程的工具,遇到问题时,这些观察工具能提供不少帮助。

本文介绍了在ShardingSphere性能问题排查和优化过程中如何入手,探讨了是否需要进行性能优化的决策过程,以及如何选择和使用如jstack、jstat、jmap和async-profiler等工具来发现和解决Java应用的性能问题,强调了性能优化的复杂性和实际场景的重要性。
最低0.47元/天 解锁文章
2237





