一场版本升级引发的性能血案 - 之数据历险


声明:本文转载自https://my.oschina.net/greenlaw110/blog/1821001,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

1. 案情回顾

老码农在 一场版本升级引发的性能血案的追凶过程 中谈到了 ActFramework 在 TFB 第 N 轮内部测试中全线溃败落入 Spring 朋友圈的不堪回首之往事. 历经三天两夜追凶过程, 老码农终于定位到了一 ActContextBase.<init> 中以 500K 字节初始化 S.Buffer 为首的犯罪集团, 并提交了新的 PR, 最新的内部测试结果已经收到果然让 Act .................................................... 某些部分重回性能巅峰. 还是上图先:

1.1 JSON 测试结果比较

TFB JSON - act-1.8.2

image

TFB JSON - act-1.8.8-RC6

image

1.2 单次查询测试结果比较

TFB Single Query - act-1.8.2

image

TFB Single Query - act-1.8.8-RC6

image

1.3 多次查询测试结果比较

TFB Multiple Queries - act-1.8.2

image

TFB Multiple Queries - act-1.8.8-RC6

image

1.4 数据 + 后台模板测试结果比较

TFB Fortunes - act-1.8.2

image

TFB Fortunes - act-1.8.8-RC6

image

1.5 多次数据更新测试结果比较

TFB Updates - act-1.8.2

image

TFB Updates - act-1.8.2

image

1.6 静态字串返回测试结果比较

TFB Plaintext - act-1.8.2

image

TFB Plaintext - act-1.8.8-RC6

image

数据不会说谎, 可以看出老码农的努力让 ActFramework 在 JSON/Plaintext 两项测试中重回性能王座. 但是 SQL 数据库的各项测试还是全线溃败, 这有点令老码农干瞪眼儿了. 不过老码农是那种轻言放弃的人么. 啥也不说了, 挽起袖子开始撸吧.

2. 数据历险

从上次的教训中我们已经知道了 JProfiler 是一个更好的性能检测工具. 所以我们还是继续依赖 JProfiler 为我们提供分析数据.

2.1 获得历史对照数据

先找出最近一次没有性能降级问题的 SNAPSHOT, 感谢 TechEmpower 为我们提供的 tfb-status 报告, 我们很容易找到最近一次没有出现问题的版本为 88180d6d3e3aded7682c653f3f7766b578b16835. 运行下面的命令检出该版本:

luog@luog-X510UQR:~/p/TEB$ git co -b 1.8.1 88180d6d3e3aded7682c653f3f7766b578b16835  Switched to a new branch '1.8.1' 

ActFramework 为 TFB 数据访问测试提供了多项组合, 包括:

  • ebean + mysql
  • ebean + postgresql
  • hibernate + mysql
  • hibernate + postgresql
  • eclipselink + mysql
  • eclipselink + postgresql

这些组合的测试成绩同步下降, 有充分理由相信他们都是有相似的原因引起的. 老码农选择了 eclipselink + mysql 组合来分析性能下降的原因, 主要原因是 eclipselink 是标准的 JPA 实现, 而且比 hibernate 要简单一下(只是比 hibernate 简单一点点, 后来的经历让老码农充分了解了数据库访问的复杂程度).

下面我们就按照该组合来 build 运行版本, 并启动应用:

luog@luog-X510UQR:~/p/TEB/frameworks/Java/act$ mvn -q -P eclipselink_mysql clean package luog@luog-X510UQR:~/p/TEB/frameworks/Java/act$ cd target/dist luog@luog-X510UQR:~/p/TEB/frameworks/Java/act/target/dist$ unzip actframework-1.8.1-r88180d.zip luog@luog-X510UQR:~/p/TEB/frameworks/Java/act/target/dist$ ./run -p eclipselink_mysql Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=128M; support was removed in 8.0         _  ___   _   _                _           _    _         /\   /    |   |_  |_)   /\   |\/|  |_  \    /  / \  |_)  |/   /--\  \_   |   |   | \  /--\  |  |  |_   \/\/   \_/  | \  |\                                                                                           powered by ActFramework r1.8.1-6fcc0   version: r1.8.1-88180d scan pkg: com.techempower.act base dir: /home/luog/p/TEB/frameworks/Java/act/target/dist      pid: 25323  profile: eclipselink_mysql     mode: PROD       zen: There should be one-- and preferably only one --obvious way to do it.           Although that way may not be obvious at first unless you're Dutch. 

下面就先用 wrk 生成两次 15s 的压测请求, 预热 JVM

luog@luog-X510UQR:~$ wrk -t4 -c128 -d15s http://localhost:5460/db Running 15s test @ http://localhost:5460/db   4 threads and 128 connections   Thread Stats   Avg      Stdev     Max   +/- Stdev     Latency    16.36ms   27.94ms 342.41ms   91.27%     Req/Sec     3.54k     1.85k    9.32k    58.60%   203184 requests in 15.09s, 36.00MB read Requests/sec:  13462.55 Transfer/sec:      2.39MB luog@luog-X510UQR:~$ wrk -t4 -c128 -d15s http://localhost:5460/db Running 15s test @ http://localhost:5460/db   4 threads and 128 connections   Thread Stats   Avg      Stdev     Max   +/- Stdev     Latency    10.32ms   16.61ms 216.92ms   91.24%     Req/Sec     5.11k     1.91k   18.69k    81.13%   302010 requests in 15.08s, 53.51MB read Requests/sec:  20029.88 Transfer/sec:      3.55MB 

接下来就是启动 JProfiler 连接刚刚启动的进程:

image

JProfiler 在启动的时候会询问是否 instrument 还是 sample. 这里我们选择 instrument, 因为需要监测的部分我们是很清楚的

image

需要注意的是 Filter 的定义对于 instrument 非常重要. 我们在这里选择对这个测试更加重要的类, 简单地说就是和数据库访问相关的类:

image

在上面的对话框中我们选择了一下 package:

  • act.db
  • javax.persistent
  • org.eclipse.persistent
  • osgl.util
  • com.mysql
  • com.zaxxer.hikari

一共有超过 4000 个类, 其中绝大部分都是 eclipselink 的.

同时我们也打开数据库 probe 设置:

image

JProfiler 开始之后运行 wrk 来生成压测请求 30 秒:

luog@luog-X510UQR:~$ wrk -t4 -c128 -d30s http://localhost:5460/db 

下面就是 JProfiler 捕获到的数据:

image

我们先保存为 SNAPSHOT, 等收集到有问题版本的数据之后对二者做对比.

2.2 获得最新数据

还是通过 tfb-status 拿到最近的版本, checkout 出来运行:

luog@luog-X510UQR:~/p/TEB/frameworks/Java/act$ git co -b RC6 aa59d4e36a24d88f7bd09f1798793893c246e477  Switched to a new branch 'RC6' luog@luog-X510UQR:~/p/TEB/frameworks/Java/act$ rm -rf target luog@luog-X510UQR:~/p/TEB/frameworks/Java/act$ mvn package -q -P eclipselink_mysql luog@luog-X510UQR:~/p/TEB/frameworks/Java/act$ cd target/dist/ luog@luog-X510UQR:~/p/TEB/frameworks/Java/act/target/dist$ tar xzf teb-act-eclipselink-mysql.tar.gz  luog@luog-X510UQR:~/p/TEB/frameworks/Java/act/target/dist$ !. ./run -p eclipselink_mysql Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=128M; support was removed in 8.0         _  ___   _   _                _           _    _         /\   /    |   |_  |_)   /\   |\/|  |_  \    /  / \  |_)  |/   /--\  \_   |   |   | \  /--\  |  |  |_   \/\/   \_/  | \  |\                                                                                        powered by ActFramework r1.8.8-RC6-c480   version: r1.8.8.2-aa59d scan pkg:  base dir: /home/luog/p/TEB/frameworks/Java/act/target/dist      pid: 23873  profile: eclipselink_mysql     mode: PROD       zen: Complex is better than complicated. 

按照先前的方式先压测两次 15s 预热 JVM:

luog@luog-X510UQR:~$ wrk -t4 -c128 -d15s http://localhost:5460/db Running 15s test @ http://localhost:5460/db   4 threads and 128 connections   Thread Stats   Avg      Stdev     Max   +/- Stdev     Latency    10.52ms   21.48ms 325.82ms   95.75%     Req/Sec     4.29k     2.18k   12.72k    75.00%   252609 requests in 15.08s, 36.57MB read Requests/sec:  16746.37 Transfer/sec:      2.42MB luog@luog-X510UQR:~$ wrk -t4 -c128 -d15s http://localhost:5460/db Running 15s test @ http://localhost:5460/db   4 threads and 128 connections   Thread Stats   Avg      Stdev     Max   +/- Stdev     Latency     7.98ms    9.50ms 123.31ms   86.39%     Req/Sec     5.33k     2.11k   14.22k    82.67%   319193 requests in 15.06s, 46.20MB read Requests/sec:  21188.63 Transfer/sec:      3.07MB 

JProfiler 也是按照先前的方式连接上面的进程, 开始监测. 同时跑 wrk 生成压测请求 30 秒, 结果如下:

image

下面对两次测试的 CPU Hot Spots 做比较:

1.8.1 (Good one)

image

1.8.8-RC6 (Bad one)

image

看不出什么东西, 对吧. 看不出就对了, 因为老码农也是一脸懵比. 不过发现一个现象, 两个 Session 都有很多方法, invocation 次数超级多, 但是 self 时间非常短:

image

也许把这些方法过滤掉会有一些发现. 说干就干, 重新起一个 JProfiler Session, 把上面那些方法都加到 Session Setting 的 Ignore Methods 当中: image

搞定这些设置之后从头再撸一遍, 情况是这样的:

1.8.1

image

1.8.8-RC6

image

还是不清楚为啥 RC6 比 1.8.1 速度降低了很多, 也许是老码农的 8550U 的机器不够强劲, 反映不出问题吧. 不过还是让老码农逮到一个比较有趣的地方, 两个 session 都有超过 20K 的 com.mysql.jdbc.connectionImpl.prepareStatement 的调用. 这个让老码农很奇怪, PreparedStatement 不应该是重复利用吗?

老码农决定试试 JProfiler 的 Sampling 方式, 又重头开始撸 (这种撸法简直是要人命的节奏啊). 最后得出的情况是:

1.8.1

image

1.8.8-RC6

image

貌似更加清晰一点, 但依然看不出性能下降的原因. 但还是有收获的, 这个 readonly 的请求怎么会在有 ConnectionImpl.rollback 的情况. 结合上次发现的 prepareStatement 调用, 不需要问元芳, 老码农就能断定此中必有蹊跷. 本来以为可以停歇了, 没想到采坑的旅程才刚刚开始. 为了不耽搁大家阅读时间, 就直接看结局吧

3. 结局

老码农第一个深入研究的问题: 为啥 Connection 会 rollback, 这与 prepareStatement 不停调用有何关联, 结果是给 Eclipselink 项目提了一个 Issue 报告, 具体内容也是 TLDR, 欲知详情, 移步这里: https://github.com/eclipse-ee4j/eclipselink/issues/107

那 rollback 的问题来自于 act db 总是建立事务, 对于只读的请求最后调用事务的 rollback, 这个额外开销不能接受, 经过三天两夜连续撸? (噢, 说错了, 是撸码), act 现在可以在数据源配置上只读数据源了, 对于 TFB 项目, 现在的配置是这样的:

db.username=benchmarkdbuser db.password=benchmarkdbpass db.isolationLevel=1 db.maxConnections=128 db.minConnections=64 db.slave.maxConnections=256 db.slave.minConnections=128 db.poolServerPrepStmts=true ... 

看到其中的 db.slave 了吗? 那就是为只读操作准备的, 只读数据源没有特殊设定的都继承主数据源设定. 看官一定会问, 怎么才能知道应该使用只读而不是主数据源, 这个问题的答案是分两种情况:

  1. 用户显式制定事务类型, 例如:
@Transaction(readonly=true) // specify the transaction is readonly @GetAction("/db") public World singleQuery() {...} 
  1. 如果用户没有指定事务, 则系统从 Dao 操作来判断, 对于 select, count 等操作, 都不启动事务, 对于 save, update, delete 等操作则判断是否存在事务, 如果没有, 则启动读写事务.

这次的数据历险触发整个 act-sql-common 包括 ebean 和 JPA 的架构完全重写, 而且让老码农采坑无数, 差点就精尽人亡, 不过结果 .................................... 结果老码农也不知道啊, 已经升级了 TFB 项目, 提交了 PR, 需要再等差不多 100 个小时之后就能见分晓了, 希望能给有个惊喜.

咱们下回分解

本文发表于2018年05月30日 12:00
(c)注:本文转载自https://my.oschina.net/greenlaw110/blog/1821001,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 1896 讨论 0 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1