性能优化实战

项目背景:在SDN控制器中,本团队作为的数据的中心,负责将物理层和IP层得到数据进行汇总,建立数据之间的联系,然后将数据持久化。

数据之间的关联关系建立以及数据合法性校验和快速入库成为了主要任务。当数据量特别大情况下,控制器也需要能够快速响应业务。对于本次优化

主要针对系统在首次部署的时候,千万级数据的情况下,部署快,消耗内存少作为主要目标。

前期准备

(1)做性能优化首先需要工具,目前主流的阿里开源的arthars ,以及jdk自带的命令,还有收费版的jprofile,不得不说,jprofile是真的强,贵有贵的道理

(2)熟悉业务代码,熟悉整个微服务系统启动的流程,作为SDN控制器的数据中心,启动流程主要有二层网元的收集入库(微服务1)-->二三层端口的收集入库(微服务1)-->二层链路的收集入库(微服务1-->环系统的收集入库(微服务1)-->三层节点和链路的收集校验和入库(微服务2)-->二三层层次关系的推导与入库 --->网元上某属性的收集更新入库(微服务3提供)--->端口某属性的收集更新--->微服务提供

(3)搭建好环境

调优开始

(1)通过jprofile可视化界面结合系统的日志,可以看到整个系统从开始运行到结束,系统在二三层入库的时候频繁的发生full GC 以及在二三层layer关系建立的时候,达到系统的内存峰值。整个系统运行大部分耗时发生在二三层端口入库。

(2)结合业务进一步分析,二三层端口一共有三张表,每张表数量600W的数据,600W个java操作模型对象,底层采用insert into语句入库,尝试调参了,600w数据*3张表采用自研框架入库大概30多分钟

结合mybatis框架测试(2000一批)也需要24分钟。虽然不知道这个数据在业界是什么水平,但是肯定不行啊。看到PG官网说,copy性能比较高,但是copy的话只适用于插入,同时需要将数据转化为流的形式

这里在使用copy的时候,踩了很多 坑啊,数据格式,以及数据写入的时候,又字符串逃跑了,最后肯定都解决了

(3)开始开发copy入库的代码,开发完成后发现还不错下降到15分钟,600万数据*3张表  其中还包含了模型的转化时间。但是整个系统启动的时候还不能满足要求啊,看到书上说,索引会影响入库的效率,好了,一个想法出来了,能够在数据入库完以后再建立索引呢,当然可以了,于是把建立索引的顺序放到600w数据入库完成后。测试发现咿呀,数据入库就花了5分钟。但是再额外建立索引需要7分钟的,但是额外建立索引可以采用异步呀。

(4)优化到这里,jprofile查看内存还是很高啊,600w个数据一次性取出来,这个大对象肯定会直接放入老年代的,而且后面模型的转化会占用内存,咋办呢,分批 分批 分批

(5)怎么分批呢,这600个对象来源是一个微服务,可以一次只需要取其中100w,取完这100w后,进行处理入库,然后再去取其他的数据。

(5)因为端口是挂在网元上,将入库的网元分批,端口自然而然就分批了,分批后遇到的问题就是线程池的使用了,因为原来一起的,直接放入线程池,但是现在分批了,线程池在方法内执行会频繁创建和销毁啊,这个也没有办法了,直接使用的ForkJoinPool把。结合java新生代的大小,看看分批,一下分多少比较合适,好了,600w数据,大概分了5批,这5批是串行还是并行呢,最后选择了串行,因为并发的话,内存可能又上去了,而且在入库的时候,到了数据库层面,连接池的数量是有限的,且数据库同一张表,可能底层会加锁,将整个表锁住。

(6)查看jprofile,内存下降了30%啊,原来需要35G,端口峰值现在只需要21G,(老板要求在30G以下)可能听起来还很大,但是毕竟有这么多数据啊,还有其他东西。

(7)但是layer关系还需要25G啊,这不行啊,查看业务代码,发现内存中有端口600W的数据,但是这个一个数据有15列左右呢,我只需要2列,本来使用自研ORM框架,但是这里想到了mybatis,由于OSGI环境,没法直接用,但是我可以只取2个字段啊,数据库的层面查找也快了。

(8)又发现从微服务4取端口数据更新的时候(后台异步执行的),呀这一个需要1个小时啊,内存也在30多G,又GG了,继续优化,发现这里更新的时候,会按照网元去查找端口,找完然后更新入库,这个方法有点长啊,虽然对于小数量的情况下性能还可以,但是批量了肯定就不行了,现在可以一批网元的找,找完在更新入库,这里明显的时间肯定下去了,因为最少GC的次数少了,而且对数据库访问的次数也少了,优化完成后,缩短到20min。就是600W数据的update(就更新2个字段)20min,但是通过后台日志,发现去数据库取都需要花8min,这里查询后续继续优化。

(9)系统的首次启动和部署从最开始的1小时,缩短到了20min  (8中的属于后台异步任务,不考虑在内)

(10)但是数据的重新同步又是个问题 ,600w数据的同步,首先要去数据库看看这600W个对象取出来,跟原始数据做一个对比,这就麻烦,首先数据库中600W对象,加原始对象600w这不得直接OOM嘛,当然后台一直频繁FULLGC。想到第一个方案,仍然是分批,按照刚才思路分批,但是对比也消耗存啊,这里想到一个办法,就是数据库MVCC中,多版本控制,我能不能在数据中多存一列,作为它的版本号,就是这个数据update了,在update之前,我先把数据做一个hash,然后存入进去,后面做数据重新发现的时候,我只要取出ID和版本号,出来,一方面内存下降了很多,如果一样,我就不做处理,不一样的话,我就做更新动作,因为有大部分对象都是没有发生变更的。

(11)妈呀,内存直线下降,以前要OOM,现在在老板要求的内存下,爽歪歪的跑着。这里当然也踩了不少坑了。

(12)记录个sql查询的优化把:主要就是PG执行去分析,个人感觉不是100%准确的,主要就是让查询尽量走索引,但是千万不要创建很多索引,索引一个字段能解决不要用两个,比如性别跟项目组,个人觉得用项目组就可以了嘛,而且索引更新消耗时间啊。而且能查一个字段不要查两个,为啥呢,避免回表。

(13)感觉优化就是停不下来啊,我个人想优化到15G以下

后续的优化思路(会继续走下去的)

(1)优化线程池的使用,看到系统后台的等待线程最大到了200个,不知道为啥这么多呢,继续看业务代码,到底为啥创建这么多线程

(2)GC啊,系统还是存在不少FullGC的,这肯定不行啊

(3)有线程阻塞,这个在同步三层数据的时候,线程池里面有不少线程处于阻塞状态,这应该不合理把,继续撸业务代码

(4)模型转化啊,以前为了业务方便就是别的 微服务模型转为操作模型 在转化为存储模型,这里应该可以统一提供一个框架,就是模型的转化,当然可以继续使用mapStruct 

(5)目前我们服务,有一个自研的内存数据库,除了那600W数据,很多数据在内存数据库中也存了一部分啊,这个后续考虑只存储索引和主键等关键信息?

(6)目前大部分都是在业务测做的处理,最后再考虑GC算法和一些参数把。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值