`

记一次代码优化(大数据量处理及存储)

阅读更多
记一次代码优化过程
--- 大数据量的处理及存储

1. 原始场景再现:
该模块主要是客户端负责上传一个包含手机号码的txt,其中一行一个手机号码。服务端读取并解析该文件,解析过程中需要做有效性验证。例如:号码位数,是有效数字及是否在有效号段之内。最后保存数据到DB。
该包含手机号码文件数据在20W到200W之间。

2. 问题所在
在客户端上传20W数据的时候,后台相应很慢,查看后台的CPU及内存
mpstat -P ALL 1   //查看LINUX系统内存及CPU的消耗情况
发现CPU一直处在100%状态下,而且消耗的时间很长。近十分钟也没有回复到客户端。

3. 问题详解
首先查看代码。列下原代码思路:
1) 使用apache的公用包来处理文件的上传,保存客户端文件到服务器
2) 打开读取文件IO,及写日志IO,读取文件信息,到一个LIST中。
3) 双重遍历LIST,进行查重操作。重复数据记录到日志文件中
4) 遍历LIST进行有效位数的校验。重复数据记录到日志文件中
5) 遍历LIST进行是否为有效数字验证。重复数据记录到日志文件中
6) 遍历该LIST,组装为数据库存储对象传递到DAO层,DAO层再次遍历对象容器,将插入对象添加到了批量提交的LIST中。最后将20W的数据一次性批量提交

4. 问题解决思路

第一次改动:仔细查看代码我们可以很清晰的看出,问题主要在两块:
一是数据的有效性检查,原代码采用了多次循环遍历的方式处理,很耗CPU。故首先将多次遍历的处理应该缩减为一次遍历即可,但是仔细想想其中存在一个查重的处理操作,故我们将原本用list存储的方式改为SET存储的方式,因为set不会存储重复的数据,这样可以达到查重的效果。

Key:HashSet底层使用hash数组实现的,其原理就是当保存一个对象的时候,首先调用该对象的hashCode方法,获得hash码与原数组及数组子链表中的数据进行比较,若是相同的话则不进行插入操作,再不存在的情况下,才进行存储。由于String 类型已经实现了hashCode方法,所以我们不需要实现该方法若是其他类型的对象我们则需要实现该方法。

二就是数组存储的地方了,原代码采用了几十万条数据的一次批量提交,当然很消耗资源,代码回复给客户端慢。这里我采用了ORACLE写一个存储过程,JAVA端传递一个数组给ORACLE,有存储过程来处理大数据量,这样就将服务器的压力转移到ORACLE安装的的那台服务器。
好的,第一次改动过完成。重启TOMCAT试下。结果当我们只上传20W数据的时候,发现还是很慢很慢,回头又仔细看代码,打断点。再调试的过程中,发现在我只采用一个遍历循环的时候,CPU就一直处理100%,原来在处理这20W数据的时候,CPU就一直处理很高的状态了,那该怎么办呢?这个时候就是第二次改动了
------------------------------------这里是华丽的分割线-------------------------------------
第二次改动:
个人经验,一般处理这种大数据量有两种方法,其实这两次方法的本质是一样的,就是为了降低CPU。
第一种是在我们在遍历循环的时候,在循环遍历到一定数量的时候,进行Thread.sleep(5)操作,带该线程睡眠片刻

For (int I =0 ;I < list.size();i++)
{
	//…业务处理
	If (3000 == i)
{
		Thread.sleep(5)
	}
}

强制CPU暂时不处理该线程。

第二种方法就是采用流控的方式,流控的原理就是我们有一个初始量,循环过程中我们累加这个量,当达到一定量的时候,该线程进行wait()操作,同时我们启动一个定时器,定时间周期对该变量进行清零操作,并唤醒该线程。其中可能会出现两种情况,一是我们定时器的清零操作还没到,累积量就已经到了,那么该线程就会处在等待状态,等待清零时间到,唤醒线程。二是累积量还没到,清零时间就到了,对线程进行清零,那么这个时候线程会一直处在一个运行状态,如果这个时候CPU使用率很高的话,就达不到我们需要的效果,所以该方式的控制就需要实际调整定时器的扫描周期、记累积量的值设定。实际过程中需要调整两参数。
因为我们是将流控写成了一个工具类,所以不好贴出来,具体也就是上面所所得思路。
比较两种方法我们明显可以看出,采用流控的方式更佳,因为它是不阻塞线程,wait的方式。第一次则是sleep,阻塞线程。
好,第二次改动已经完成了,我们就开工试试了。当你怀着美好的愿望时,老天总是不能让你如意。结果很失望,还是慢的一塌糊涂,然后不断的调整参数,结果还是没降下来。这个时候我就开始反思了,难道这种方式有问题,再去打断点调试的过程发现,采用了流控后,在处理数据的时候是CPU确实是降下来了,但是还是存在一个问题,再没处理完一条记录后,都会有一个日志的记录,这个时候用的是bufferRead,结果我们在最后的close()IO的时候,一次行将内存缓存中的数据库刷到文件,导致CPU及内存的过高。知道原因后我开始了第三次的改动。
-------------------------------------分割线又出现了--------------------------------------
第三次改动:
这个时候,我采用了流控相同的思路,就是在处理完一定数据量的数据后,我们进行一次flush操作,将内存中数据刷到文件中,免得一次性刷,同时因为我们一般是整数的W级数据,所以定义一个常量为3000(全局多个地方需要刷数据,好改动),作为flush标示。因为采用3000的话,最后一次我们刷的只有1000的数据量,同时关闭IO需要开销,所以能够在一定程度上降低CPU,同时发现在第一次flush的时候CPU会较高,越后CPU会越低,不知道什么原因。
If (…)
{
		if (3000 == i)
{
		read.flush()l
}
}

OK ,又可以开工进行测试了,我是费了九牛二虎之力开动了TOMCAT(机子太差),哈哈,终于成功了。CPU从原来的100%降到了10%左右,很有成就感,嘿嘿。这样就将给测试区测试了。但是,但是,测试告诉我一个很不幸的消息,CPU还是很高,这个是为什么呢?
仔细看看了,原来TMD应用的服务器跟DB在一台服务器上,气死老夫了。采用一次性传递数据的片刻,ORACLE会使CPU会飙到100%,而且处理这么多数据的时候也会持续5s左右的时间一直处在该位。这个时候咋办呢?这个就需要第四次改动了。
-----------------------------我是分割线,大家好!北边的朋友在哪里?--------------------------
第四次改动:
这个时候,首先改动的是存储过程,采用了动态变量绑定,同时调整commit;的次数,在1000,3000,5000,10000条记录的时候提交数据,结果调试后,没什么改观,没则,只好换个思路。
启用存储过程,在java端想办法,最简单的方法还是启用原来的批量提交方式,修改为一定数量数据后提交,因为这个提交,并在提交后sleep,方式CPU一直处在这个状态。
伪代码就是
If ()
{
	If (3000 == i)
{
		//提交数据到DB
	//清零标示,清空临时list
	Thread.sleep();
}
}

就这么简单。提交调试后,发现java端不会消耗很高的CPU,在第一次提交批量数据时会到30%左右,第二三次会降到15%左右,同时ORACLE消耗的CPU在第一次会100%,但是持续时间很短,就1s的时间,在达到这样的效果后,领导说可以了。总算结束了这段优化过程。
简单的说,我这种方式不过是用时间换CPU的性能,只使用与对于时间要求不是很高,更要求CPU的性能的场景。








分享到:
评论
25 楼 fengmy123 2010-04-29  
觉得用临时表快点,反正就一字段,java只需要验证有效性就算了,重复数据的处理也让数据库去做吧。
24 楼 inter12 2010-02-22  
zhangdp_neu 写道
"该模块主要是客户端负责上传一个包含手机号码的txt"

很感兴趣你的优化,txt格式是什么样的?

txt就是简单手机号码
一行一个号码
23 楼 inter12 2010-02-22  
强强爱妍妍 写道
"对于时间要求不是很高,更要求CPU的性能的场景"

第一次听见这个说法,hoho

个人经验中,当你的代码操作20w数据的时候,应该有大量内存操作,cpu不应该到100%的

常见cpu 100%原因
1 死循环
2 锁等待

就java来说,锁等待的可能性比较大. 比如不小心用到了有同步能力的class.
望楼主再检查一下

该问题还真的是因为CPU过高,我没做文中提到的优化时,CPU在100%,而内存在正常范围之内。
这个数据时通过jconsole观察的。应该没错。
至于你说的锁等待在本案可能不大。
22 楼 inter12 2010-02-22  
gexp.fang 写道
主要是因为处理效率低还是因为CPU瞬间达到100%而惹你不高兴了呢?

主要是测试提出一个问题单,说在执行操作时候CPU过高,仅仅是为解决这个问题。
21 楼 gexp.fang 2010-02-22  
主要是因为处理效率低还是因为CPU瞬间达到100%而惹你不高兴了呢?
20 楼 强强爱妍妍 2010-02-22  
"对于时间要求不是很高,更要求CPU的性能的场景"

第一次听见这个说法,hoho

个人经验中,当你的代码操作20w数据的时候,应该有大量内存操作,cpu不应该到100%的

常见cpu 100%原因
1 死循环
2 锁等待

就java来说,锁等待的可能性比较大. 比如不小心用到了有同步能力的class.
望楼主再检查一下
19 楼 jciwolf 2010-02-22  
可不可以将文本先导入到数据库之中的临时表中,然后用存储过程处理。
18 楼 zhouky4665 2010-02-21  
inter12 写道
zhouky4665 写道
直接用sqlload直接导入数据库,然后写个存储过程处理业务逻辑,比如去重、有效性检查。
这样非常快,200w数据最多2分钟搞定

问题是用sqlload的是需要将数据文件存放在服务器中,那是否需要定义一个crontab来扫描这些文件?
而我们需要实时的将数据保存到数据库中。

sqlldr可以不在oracle服务器端,在你web服务器端也可,不过要安装oracle客户端。不用crontab,文件上传完成后直接调用sqlldr命令即可。
17 楼 inter12 2010-02-21  
抛出异常的爱 写道
inter12 写道
抛出异常的爱 写道
thread.setPriority
大过年的


设置线程的优先级,好像从来没起作用过

代码里需要加入yield();
不然不会把cpu让给别的线程的的。
而且你的程式问题出在了内存占用过大的问题上
使用流读入每次都只运行一条记录
对于排重前阵子刚刚用过Bloom Filter基本上可以用来排重
对于大数据应该不需要总回滚。
可以减少事务边界(3000条一commit)



JDK1.5以后就不推荐使用yield方法了。
而本案的方法主要是为了降低CPU及内存,只要将这些资源将下来就可以,倒不在乎相应的快慢
我这里的排重用的是hashSet,本省就已经自带了排重效果。
Bloom Filter没有用过,有时间研究下。
同时我在体中已经写明了 是5000数据COMMIT一次。
16 楼 inter12 2010-02-21  
beckrabbit 写道
客户端要上传的这个20W到200W数据的文件也是程序生成的吧,为什么不优化客户端,每批少量,分多次上传呢?

20W已经是最低的数据量了
15 楼 inter12 2010-02-21  
zhouky4665 写道
直接用sqlload直接导入数据库,然后写个存储过程处理业务逻辑,比如去重、有效性检查。
这样非常快,200w数据最多2分钟搞定

问题是用sqlload的是需要将数据文件存放在服务器中,那是否需要定义一个crontab来扫描这些文件?
而我们需要实时的将数据保存到数据库中。
14 楼 Ihavegotyou 2010-02-20  
Thread.yield(); //这个是否效果更好?
13 楼 spyker 2010-02-19  
1个txt文件100w数据
12 楼 spyker 2010-02-19  
我做过类似的东西
我的是100w左右
11 楼 songfantasy 2010-02-19  
lz是做电信行业的吧。移动boss
10 楼 抛出异常的爱 2010-02-18  
inter12 写道
抛出异常的爱 写道
thread.setPriority
大过年的


设置线程的优先级,好像从来没起作用过

代码里需要加入yield();
不然不会把cpu让给别的线程的的。
而且你的程式问题出在了内存占用过大的问题上
使用流读入每次都只运行一条记录
对于排重前阵子刚刚用过Bloom Filter基本上可以用来排重
对于大数据应该不需要总回滚。
可以减少事务边界(3000条一commit)


9 楼 harbey 2010-02-18  
zhouky4665 写道
直接用sqlload直接导入数据库,然后写个存储过程处理业务逻辑,比如去重、有效性检查。
这样非常快,200w数据最多2分钟搞定

我会采用这种方法,再给一字段标示数据是否已经处理(避免每次都验证已验证的数据)
8 楼 zlowly 2010-02-16  
<p>如果格式方面可以严格定义的话,考虑使用Oracle的外部表来读取这些文本文件也很好。</p>
7 楼 beckrabbit 2010-02-16  
客户端要上传的这个20W到200W数据的文件也是程序生成的吧,为什么不优化客户端,每批少量,分多次上传呢?
6 楼 FeiXing2008 2010-02-16  
不清楚你这人是快了还有什么其他作用。。。

相关推荐

    经验大数据量处理及存储代码优化过程.pdf

    。。。

    经验大数据量处理及存储代码优化过程.docx

    。。。

    经验大数据量处理及存储代码优化过程 (2).docx

    。。。

    经验大数据量处理及存储代码优化过程 (2).pdf

    。。。

    计及源荷不确定性的综合能源生产单元运行调度与容量配置随机优化模型MATLAB程序

    MILP)算法,以全生命周期内经济成本最低为优化目标,考虑物料及能量平衡约束,实现典型周内各设备功率的最优逐时调度优化,并得到最佳综合能源系统中碳捕集+电 制氢+甲烧化+氢存储+CO2存储的容量配置结果。...

    mysql数据优化详细教程

    在实际生产环境中,由于数据库本身的性能局限,就必须要对前台的应用进行一些优化,来降低数据库的访问压力。频繁的创建关闭连接,是比较...分布式数据库架构适合大数据量、负载高的情况,它有良好的拓展性和高可用性。

    oracle的sql优化

     大数据量表尽量要避免全表扫描,全部扫描会按顺序每条记录扫描,对于&gt;100万数据表影响很大。  Oracle中通过RowID访问数据是最快的方式  对字段进行函数转换,或者前模糊查询都会导致无法应用索引而进行全表扫描 ...

    购物商城系统源代码--036

    数据结构更经过精心的设计,从字段到表的分配、索引的构建、都经过缜密的考虑、相同数据量的购物系统,时代商城购物系统占用的数据库容量和其他类似产品相比要小,程序内核中查询遵循 ANSI SQL 规范。 模板体系 ...

    Java优化编程(第2版)

    Java优化编程(第2版)通过丰富、完整、富有代表性的实例,展示了如何提升Java应用性能,并且给出了优化前与优化后的Java应用程序的性能差别,以实际的实例与数字告诉你,为什么不可以这么做,应该怎么做,深入分析...

    SQL查询安全性及性能优化

    系统运行中,数据量扩大,访问量增多,蹩脚的SQL危害开始显露 低效SQL的危害 系统响应变慢,软件开发中的8秒定律,当打开一个软件或网页超过8秒时间还没有显示,则响应太慢。 死锁,当不同用户都访问某些资源的...

    asp.net数据存储与交换系统设计(源代码+thesis).zip

    这包括使用高效的算法和数据结构、使用缓存技术和数据库索引、进行代码优化和资源管理等。 通过采用这些技术方案,我们的项目将能够提供一个高性能、可扩展和可靠的Web应用程序。我们将遵循最佳的软件开发实践,进行...

    JAVA上百实例源码以及开源项目源代码

     用JAVA开发的一个小型的目录监视系统,系统会每5秒自动扫描一次需要监视的目录,可以用来监视目录中文件大小及文件增减数目的变化。 Java日期选择控件完整源代码 14个目标文件 内容索引:JAVA源码,系统相关,日历,...

    Java毕业设计-基于springboot开发的科研工作量管理系统设计与实现-毕业论文(附毕设源代码).rar

    后端则通过Spring Boot框架的强大功能,实现了数据的持久化存储、业务逻辑的处理以及安全性的保障。 在实现过程中,项目注重代码的可读性和可维护性,采用了规范的命名规则和注释方式,使得代码易于理解和维护。...

    kd树数据结构实现k-means聚类算法加速

    主要的方法就是,将数据存储在kd树这种空间数据结构中,树的思想类似于二叉搜索树、红黑树等,真的很强大(相信童靴们明白的)。改进的k-means方法主要考虑了合理的初始候选质心的方式,并且利用kd树的特性,减少了...

    python数据分析随书代码

    python数据分析/(印尼)伊德里斯(Idris.I.)著,韩波译。 资源包括所有章节的示例代码。需要用到python2和pip。 编辑推荐 实用的Python开源模块的大集合; 简单易懂、示例丰富的数据分析教程; 掌握数据可视化...

    MySQL 快速删除大量数据(千万级别)的几种实践方案详解

    笔者最近工作中遇见一个性能瓶颈问题,MySQL表,每天大概新增776万条记录,存储周期为7天,超过7天的数据需要...后经过研究,最终实现了飞一般(1秒左右)的速度删除770多万条数据,单张表总数据量在4600万上下,优化过程

    java源码包---java 源码 大量 实例

     用JAVA开发的一个小型的目录监视系统,系统会每5秒自动扫描一次需要监视的目录,可以用来监视目录中文件大小及文件增减数目的变化。 Java日期选择控件完整源代码 14个目标文件 内容索引:JAVA源码,系统相关,日历,...

    单片机如何精简代码量以及提升运行速度技巧介绍.docx

    3. **优化数据结构**: - 使用最紧凑的数据类型(如char而非int存储较小的数值)。 - 避免过度包装,简化数据结构层次。 4. **减少函数调用开销**: - 减少不必要的函数调用,特别是小型且频繁调用的函数。 - ...

    数据库设计与优化.pdf

    1.3.6 数据库物理存储和环境的设计 在设计阶段,可以对数据库的物理存储、操作系统环境、网络环境进行必要的设计,使得我们的系统在将来能适应比 较多的用户并发和比较大的数据量。 这里需要注意文件组的作用,适用...

Global site tag (gtag.js) - Google Analytics