|
---针对基于gorm对有参照定义collection情形---
背景
在某项目里的性能测试中,发现某一动作(下载文件)并发执行百次左右,性能大幅下降。虽然隔上几分钟之后性能恢复,但是后续请求很快又进入阻滞状态。
通过对java虚拟机的监控发现,性能降低的主要瓶颈在内存上。
根据此表现追查源代码,发现问题点竟然出在打印日志上,而此日志打印为基于gorm的mongo操作(insert)。
然而,并不是所有的基于gorm的mongo操作都存在性能问题。
这背后隐藏着什么秘密呢?
分析
首先分析本次性能问题发生的主要collection”OperationLog”
发现其中使用了几个参照。
有没有可能是大量参照的使用造成了性能的问题呢?
为了验证参照对性能的影响,我把此collection做了些变更,把所有的参照变更为String
鉴于打印日志属于基本的Insert操作,我也模拟了此操作。
验证
插入1000条,基于gorm插入旧的collection”OperationLog”
开始内存使用率680Mb
结束时内存使用1077 Mb
最佳历时11mms
最差历时13mms
插入1000条,基于gorm插入新的collection”NewnOperationLog”
开始内存使用率1032Mb
结束时内存使用1000 Mb中途上升至1100,然后下降
最佳历时11mms
最差历时13mms
结论
由此可见,domain设计时使用参照,是造成内存消耗过快的主要原因。
另外,对数据插入来说,参照与否对CPU时间影响不大。
然而,domain设计时使用参照可以大大简化代码,所以从这个意义上来说,生产性的提高是以产品性能为代价的。
再分析&再验证
那么,在上述情况下,多大的请求量会造成性能的问题呢。
插入1000条,基于gorm插入旧的collection”OperationLog”
开始内存使用率666Mb
结束时内存使用2000 Mb
当并发请求出现12个时,60s就把所有内存占用完全。
虽然阻塞1分钟后内存能回收,但是原来阻塞的请求又马上在1分钟内把内存耗光。
如果并发数为1,连续不断请求的话,则90s就能把所有内存用光。
和并发请求数多的情况相比,其区别是阻塞时间变短。
那么,对于新的collection”NewnOperationLog”,能否在一定条件下达到阻塞呢?
如果并发数为1,连续不断请求的话,则20s的时间内,内存占用增加了200mb,
不过在随后的120s的时间内,内存被缓慢回收,然后再上升,呈现规律的锯齿形。
如果增加并发数呢?
现在把并发增加到30,内存耗用状况发生基本上仍然在200 mb上下波动。
所不同的是,锯齿的波动周期变为10s.
最后结论
domain设计中,参照是耗用内存的重要原因。
在海量请求大并发的情况下,避免参照设计是避免性能(内存占用)问题的重要手段。
另外,在不使用参照的情况下,基于gorm,对cpu时间没有明显的提升。
测试环境説明
DELL R720 XD,CUP E5-2620,内存 2*8G,硬盘3T×12 RAID0
JAVA虚拟机
Java(TM) SE Runtime Environment (build 1.6.0_31-b04)
Java HotSpot(TM) 64-Bit Server VM (build 20.6-b01, mixed mode)
JAVA_OPTS=" -server -Xmx2g -Xms2g -Xmn256m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70" |
|
|