当前位置: 首页 > >

oracle单表数据量上亿_大数据量面前,毫秒必争。一次上亿数据量程序优化后的感想...

发布时间:

最*在搞一个开发一个程序,数据量很大,预计大概有上亿的数据量。程序中需要依次遍历原始数据,然后根据原始数据进行数据计算以及数据整合。





一开始项目组接到这个任务以后,一直在犹豫,生产服务器是否可以跑得动,毕竟在开发环境很难实现像生产环境那样的数据量以及服务器配置。项目组一直在论证,是否可以实现此功能,万一上线以后跑不动,或者需要跑好长时间,根本就没有办法交代。


最终项目组决定还是试试看。


首先,我们找到了一台2C4G的数据库服务器以及应用服务器,生产环境数据库是16C64G,应用服务器是4C8G。在测试环境大概造了1000万的数据量,以此数据量作为开发测试目标,于生产服务器的上亿数据量作为对标。采用多线程的方式完成。





第一次开发完成后,程序根本跑不动,应用服务器屡屡的内存溢出。不停的报java.lang.OutOfMemoryError: PermGen space。通过查询发现,mysql在执行查询操作时候,会把结果集数据首先拉到本地服务器,如此大的数据量,内存不挂才怪。所以,赶紧修改程序。



p = conn.prepareStatement(sql,ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);


p.setFetchSize(Integer.MIN_VALUE);


p.setFetchDirection(ResultSet.FETCH_REVERSE);






修改完成后,内存溢出的问题已经解决了,但是发现在执行p.executeBatch()的时候特别的慢,慢到令人发指,不可思议。后来调整了mysql jdbc连接参数,在连接参数中添加了



rewriteBatchedStatement=true



参数,开启了批量出来,原来不添加的话,jdbc还是一条一条向服务器提交。





再次发动执行,发现虽然p.executeBatch()执行速度快了,但是还是不是让人特别满意,所以又修改了sql程序拼接方法,优化insert执行语句,由原来的



insert into tables (name1,name2,...) values (?,?,....)


insert into tables (name1,name2,...) values (?,?,....)


insert into tables (name1,name2,...) values (?,?,....)



修改为



insert into tables (name1,name2,...) values (?,?,....),(?,?,....),(?,?,....),(?,?,....)



效率提升了很多。





程序处理过程中,需要有update操作,如果数据存在,需要update,之前的设计是先按照主键查询,如数据存在,则update,不存在则执行insert操作。但是效率还是不满意。所以再次修改程序,在insert语句后添加了ON DUPLICATE KEY UPDATE参数。



insert into tables (name1,name2,...) values (?,?,....),(?,?,....),(?,?,....),(?,?,....) ON DUPLICATE KEY UPDATE name1=values(name1)......



修改后,程序不用再执行先查询后判断是否需要更新等等。


程序修改到这里,其实就可以顺利完成了,1000万的数据在测试服务器6个并发的时候需要140分钟,程序每分钟可以处理7.1万笔数据。





但是,我一直在思考,是否可以再快一些,而且我发现,6个并发同时执行时,应用服务器的CPU飚的非常高,每次都可以到95%以上,而数据库服务器其实并不是非常的繁忙。这个不正常,按说数据库服务器应该很忙才对,所以可能还是有优化的地方。


出于探索的精神,项目组再次启程,首先就从CPU下手。





通过阿里巴巴的arthas工具,我们成功的开始监控某个线程操作,每隔10秒钟抓一次线程正在执行的内容,我们惊奇的发现,大部分的时候,线程都在执行同一个操作,就是Spring EL表达式解析、计算工作。


为了方便程序开发和配置,我们此采用了配置文件配置的方式,对于一些计算项,采用Spring EL表达式配置,方便项目组调试、调整。难道真的是EL表达式拖了后腿?


我们首先把所有的EL表达式都去掉,改用默认值的方式测试了一次,当程序启动时候,日志的刷新速度让我们目不暇接,20分钟就完成了批量。项目组顿时兴奋不已,就是它拖了后腿。





通过前后计算,我们处理每条数据执行了5个EL表达式,1000万行数据就是5000万次,*均计算了下,每个EL表达式需要执行7毫秒,其实也不长看起来,但是乘以5000万,那就是120分钟。


然后,项目组立即决定取消EL表达式配置的模式,采用java程序计算的方式完成需要计算的业务数据来完成,虽然可能损失了灵活配置,但是提交了执行效率,这笔账划得来。


修改后,程序在22分钟就执行完成,每分钟完成45.5万笔数据,CPU峰值降到了60%。


7毫秒,看起来真的不多,但是架不住几千万上亿次的调用。


这次调优让项目组变得很兴奋,现在项目组成员在日常开发过程中,已经养成了非常好的调优的好*惯,运行稍微有点慢或者不满意,就不停的查找、定位、解决,我仿佛已经可以看到一批高级的人才已经在冉冉升起了。因为调优真的很复杂,涉及到网络、IO、应用程序设计、逻辑梳理、数据库等等各方各面,只有不断的追求、学*才能用的好,用的精。







相关资源:Oracle中如何对超大规模数据(如超过2亿条)直接用SQL语句入库?



友情链接: