由于公司业务的快速发展导致数据库的数据量飞速增长,我们底层数据的存储,逐渐成为制约整个产品性能的核心部分。于是我们调研了各大数据库分库产品,如:hibernate5+对多租户的支持,当当ShardingJdbc,mysqlProxy,mycat等。最终考虑到国内目前各个产品的活跃度和成熟度,以及对现有业务的适配情况,最终选择了mycat作为分库分表的中间件产品。经过了几轮性能测试,发现mycat表现基本良好,性能损失率约为30%,在可接受的范围内。于是考虑在不分库分表的情况下,将mycat作为数据库代理,先在线上做个简单测试,没想到简单业务的服务没问题,但是核心服务在连接mycat时会出现请求响应缓慢的问题,一个通过主键查对象的SQL响应时间居然超过1秒钟。于是,赶紧下线mycat,重新连接mysql后,性能稳定下来。这没分库就表现这么差,分库了还了得。。。心有不甘,于是笔者踏上了mycat性能调优的汤坑之路。。
一、日志对mycat性能的影响
1.log4j.xml配置成debug级别,mycat jvm参数:Xmx4G Xms4G XX:MaxDirectMemorySize=4G
a.GC情况:
S0 S1 E O P YGC YGCT FGC FGCT GCT
88.69 0.00 77.57 5.05 � 2216 8.390 1 0.067 8.457
77.93 0.00 11.34 5.06 � 2218 8.399 1 0.067 8.466
0.00 76.96 46.41 5.06 � 2219 8.403 1 0.067 8.470
74.38 0.00 83.05 5.06 � 2220 8.408 1 0.067 8.475
60.70 0.00 18.26 5.07 � 2222 8.417 1 0.067 8.484
0.00 65.79 52.49 5.07 � 2223 8.421 1 0.067 8.488
73.44 0.00 87.12 5.07 � 2224 8.426 1 0.067 8.492
70.79 0.00 21.50 5.08 � 2226 8.435 1 0.067 8.501
每秒1~2次非常频繁
b.JVM CPU监控:
log4j占用CPU时间最长。
c.JVM MEM监控:
内存占用最多的是字符类型
d.2000个简单查询耗时统计
|
total costs(ms) |
total costs(ms) |
total costs(ms) |
avg total costs(ms) |
costs(ms) |
costs(ms) |
costs(ms) |
avg costs(ms) |
---|---|---|---|---|---|---|---|---|
mysql | 22935 | 24316 | 25853 | 24368 | 11 | 12 | 12 | 11.6 |
mycat | 173680 | 171557 | 170718 | 171985 | 86 | 85 | 85 | 85.3 |
mysql耗时统计图:
mycat耗时统计图:
根据数据分析可得:mycat比mysql慢6.07倍
2.log4j.xml配置成error级别,mycat jvm参数:Xmx4G Xms4G XX:MaxDirectMemorySize=4G
a.GC情况:
S0 S1 E O P YGC YGCT FGC FGCT GCT
84.81 0.00 70.54 1.24 � 94 2.451 1 0.064 2.515
84.81 0.00 80.04 1.24 � 94 2.451 1 0.064 2.515
84.81 0.00 85.36 1.24 � 94 2.451 1 0.064 2.515
84.81 0.00 87.42 1.24 � 94 2.451 1 0.064 2.515
84.81 0.00 89.46 1.24 � 94 2.451 1 0.064 2.515
84.81 0.00 91.69 1.24 � 94 2.451 1 0.064 2.515
84.81 0.00 93.53 1.24 � 94 2.451 1 0.064 2.515
84.81 0.00 96.10 1.24 � 94 2.451 1 0.064 2.515
84.81 0.00 97.97 1.24 � 94 2.451 1 0.064 2.515
84.81 0.00 98.69 1.24 � 94 2.451 1 0.064 2.515
0.00 92.86 13.14 1.25 � 95 2.472 1 0.064 2.536
0.00 92.86 49.48 1.25 � 95 2.472 1 0.064 2.536
0.00 92.86 68.61 1.25 � 95 2.472 1 0.064 2.536
每秒0.08次,看来日志输出是造成mycat GC频繁的元凶
b.JVM CPU监控:
log4j占用CPU的时间已经找不到了,最长的是mycat处理查询结果的线程。
c.JVM MEM监控:
调整日志级别后,int[] 对象占用的内存大大减少了
d.2000个简单查询耗时统计
|
total costs(ms) |
total costs(ms) |
total costs(ms) |
avg total costs(ms) |
costs(ms) |
costs(ms) |
costs(ms) |
avg costs(ms) |
---|---|---|---|---|---|---|---|---|
mysql | 37680 | 36239 | 36705 | 36874 | 18 | 18 | 18 | 18 |
mycat | 40554 | 39345 | 40623 | 40174 | 20 | 19 | 20 | 19.6 |
mysql耗时统计图:
mycat耗时统计图:
根据数据分析可得:mycat比mysql慢0.08倍
疑问:mycat在调整日志,重新启动后,第一次测试时发生了性能抖动,之后平缓
mycat性能抖动图:
结论:日志需要开启error级别,否则将会造成mycat GC频繁,从而影响 响应时间
二、DirectMemory对mycat性能的影响
1.log4j.xml配置成error级别,mycat jvm参数:Xmx4G Xms4G
a.GC对比
|
XX:MaxDirectMemorySize=4G |
XX:MaxDirectMemorySize=3G |
XX:MaxDirectMemorySize=2G |
XX:MaxDirectMemorySize=1G |
---|---|---|---|---|
GC | S0 S1 E O P YGC YGCT FGC FGCT GCT |
S0 S1 E O P YGC YGCT FGC FGCT GCT 72.59 0.00 55.69 2.35 � 126 2.661 1 0.066 2.727 |
S0 S1 E O P YGC YGCT FGC FGCT GCT |
S0 S1 E O P YGC YGCT FGC FGCT GCT |
gc有波动,稳定后差不多是0.33次/秒
b.DirectMemory对比
|
XX:MaxDirectMemorySize=4G |
XX:MaxDirectMemorySize=3G |
XX:MaxDirectMemorySize=2G |
XX:MaxDirectMemorySize=1G |
---|---|---|---|---|
DirectMemory |
c.2000个简单查询对比
|
XX:MaxDirectMemorySize=4Gcosts(ms) |
XX:MaxDirectMemorySize=3Gcosts(ms) |
XX:MaxDirectMemorySize=2Gcosts(ms) |
XX:MaxDirectMemorySize=1Gcosts(ms) |
---|---|---|---|---|
mysql | 18 | 18 | 17 | 15 |
mycat | 19.6 | 22 | 22 | 21 |
结论:XX:MaxDirectMemorySize的调整对性能影响不大
三、Xmx对mycat性能的影响
a.GC对比
|
Xmx4G |
Xmx3G |
Xmx2G |
Xmx1G |
---|---|---|---|---|
GC | S0 S1 E O P YGC YGCT FGC FGCT GCT |
S0 S1 E O P YGC YGCT FGC FGCT GCT |
S0 S1 E O P YGC YGCT FGC FGCT GCT |
S0 S1 E O P YGC YGCT FGC FGCT GCT |
随着内存减少,GC越来越快
b.内存使用情况
|
Xmx4G |
Xmx3G |
Xmx2G |
Xmx1G |
---|---|---|---|---|
堆内存 |
c.2000个简单查询对比
|
Xmx4Gcosts(ms) |
Xmx3Gcosts(ms) |
Xmx2Gcosts(ms) |
Xmx1Gcosts(ms) |
---|---|---|---|---|
mysql | 18 | 15 | 21 | 19 |
mycat | 21 | 18 | 26 | 24 |
四、数据库大表对mycat性能的影响
a.400多列a_account表
|
1并发(ms) |
10并发(ms) |
100并发(ms) |
200并发(ms) |
300并发(ms) |
---|---|---|---|---|---|
mysql | 3.5 | 4.6 | 32 | 61 | 85 |
mycat | 4.5 | 6.2 | 56 | 81 | 110 |
b.5列a_account表
|
1并发(ms) |
10并发(ms) |
100并发(ms) |
200并发(ms) |
300并发(ms) |
---|---|---|---|---|---|
mysql | 1.2 | 2.1 | 23 | 44 | 70 |
mycat | 1.9 | 2.2 | 22 | 45 | 68 |
通过观察2组测试数据,可以发现:
1.400多列表的那组测试中,mycat各并发下,平均都比mysql慢30%左右,
2.5列表的那组测试中,mycat各并发下,与mysql持平
3.通过观察mycat的CPU、内存、线程发现,300列的大表,占用内存非常之高,光char[]类型就达到了700M,CPU保持在30%~40%,NIO读写线程占用CPU时间最长。
而5列的小表,内存占用非常小,gc次数明显降低,CPU也稳定到了10%~20%的水平。
结论:
1.数据库表结构过大,会对mycat性能产生较大影响
2.由大表读写导致的网络流量对mycat性能影响很大,mycat宿主机需要多网卡支持
五、大表的SELECT * 对mycat性能的影响
1.通过测试大表的SELECT 全字段,和SELECT * 对比发现,SELECT 全字段时,mycat大部分的CPU都被druid SQL解析占据,yong GC主要是由于mycat解析大SQL引起的,所以ORM框架的全字段改写SQL功能(包括读写)会降低mycat性能。
2.通过将程序的SELECT全字段改为SELECT * 后,mycat GC次数直线下降,CPU也减了下来,并且响应时间得到良好的改善,基本跟直连mysql持平。
3.性能测试时,适当减小路由缓存的参数值,可以很好的减少full gc的次数。
六、调优过程遇到的坑
1.在整个调优过程中,首先出发点有可能是错误的,我们因为mycat在生产环境发生故障,而将服务的数据源切回mysql后,系统稳定下来,以此为依据断定mycat有性能问题,导致整个调优过程都将思路局限在mycat上,而忽略了自己程序的问题。
2.测试环境中,不同JVM进程会相互抢占CPU,而影响测试结果的稳定性。
3.网络带宽的大小,一度成为我们怀疑的目标,通过各种工具监控后,发现带宽不是瓶颈。
4.AWS ELB的带宽弹性功能,也是我们怀疑过的目标之一,后来通过测试,排除掉该可能性。
5.集群中多个节点同时连接mycat,导致SQL响应缓慢,让我们怀疑到了mycat客户端数量可能是性能瓶颈点。通过测试,发现连接数不是瓶颈,客户端数排除。
6.mycat内存管理有可能成为mycat响应缓慢的元凶,通过监控GC,发现yong gc次数较多,每2秒一次,Eden区很快会被填满,这让我们怀疑到可能是响应数据太大导致,从而将目标定位到数据库表结构上。
7.通过对比大表和小表在相同环境下的响应时间,可以发现小表的表现非常好,和mysql基本持平,从而断定是数据库表结构的问题。
8.由于业务已上线很长时间,不能轻易改动表结构,所以只好继续找mycat大表表现差的原因。受大表致使mycat性能下降观念先入为主的影响,调优很长时间没有思路。
9.偶然机会,同事使用JMeter来测试mycat和mysql的表现,居然发现两者性能差别不大,于是事情有了新的突破,我们开始关注为什么使用测试工具测试没事,我们用自己的服务压测就总有问题,从而将矛头对准了我们自己的底层dal程序。
10.通过使用原生的JDBC压测发现性能很好,于是将问题定位到hibernate。发现hibernate表现非常差,从而将问题锁定为hibernate。
11.通过查看hibernate的日志,发现其和JDBC直连唯一的差别在sql上,hibernate进行了sql改写,通过id查对象时,拼接了400多全列名,并且做了重命名,导致一个SQL的大小约为17K。而将这个大SQL使用JDBC直连执行时,问题得到了重现,从而定位到了性能问题的最终元凶,“hibernate改写后的长SQL”。
12.通过将hibernate通过id获取对象的hql语句改写为SELECT * FROM object后,性能表现良好,与mysql持平,自此,问题得到了解决。。
七、调优过程用到的工具
1.Linux网络带宽监控工具: nload
2.JVM内存监控工具:jstat、jvisualvm、jmap、jstack、tprofiler、jprofiler
3.mycat集群搭建工具:haproxy
4.Linux CPU监控工具:top
5.绘图工具:gnuplot
6.性能压测工具:JMeter
7.内存分析工具:EclipseMAT