Posted by Alex | Posted in OTHER STUFF | Posted on 19-06-2009
“3000万业务数据导入的性能调优始末” 这项工作开始于本周一,对即将在SAP R/3系统中导入3000w数据进行性能调优,最低目标是可以达到每天200w导入数据量,而事实上,我们舍弃了一小部分功能,从而达到了超过800w/天的导入能力。本文将向您讲述这段性能调优功能的始末。
首先我们简单说一下导入的数据,物料主数据的基础视图是先前已经创建好的,我们所要做的是扩展到所有的工厂下,因此调用BAPI: BAPI_MATERIAL_SAVEDATA,但是由于有一个视图plant stock不能通过BAPI创建,我们使用了BDC方法。
1. 问题的出现
问题出现于上周五,30w物料主数据,将被扩展到约95个工厂下,也就是说将会产生约3000w条工厂级物料数据,那么如何能保证每天200w导入能力的最低目标成为一个难题。
200w/天 ≈ 83333 / 小时 ≈ 1388 / 分钟 ≈ 23 / 秒 合 每条数据只有0.04秒的时间
而事实上,最初我接手时的程序在开发机仅能达到2.6秒/条的速度,那么很显然,2.6与0.04之间相差65倍,接近2个数量级,自然我们首先想到的就是并发,一般来讲,20进程并发,应可提升一个数量级的速度。但是我们仍然相差接近一个数量级。
2. 第一天
第一天我们做了很多无用功,但是这是后来证明的,在一开始,我们都很有信心。首先我们通过简单的观察程序和测试运行发现,有一个包含BDC过程的子程序占用了超过60%的运行时间,根据常理我们也都认可BDC速度远远低于BAPI,即使是简单的操作。因此我们把第一个重点放在了如何解决这段BDC。于是我们把第一天的全部时间都用在了如何将BDC那段功能用BAPI实现。
第一天我们三个人一直搞到晚上10点多,头晕脑胀也没能解决问题,这其间还遇到一些假象,但后来证实行不通,由于本文重点在于讲述性能优化,这段故事就不详细说了。但是结果并不理想,我们也没有达到预期的目标。
对于第一天来说,最漫长的已经过去,但最重要的还没有到来,之所以说第一天做了很多无用功,是因为第二天一大早的一个重大发现。
3. 第二天
在我们基本放弃解决BDC的问题后,我开始仔细的看程序,因为程序不是我写的,我并不是很熟悉。正当一筹莫展之际,我发现了第一个重大发现,在实现BDC功能的子程序中,居然有一条这样的语句:WAIT UP TO 2 SECONDS。这让人无法接受,后来证实,是先前一位经验不足的开发人员为了避免锁定的问题写上的,看到这里,你一定和我们当时想的一样,2.6-2 = 0.6秒,3600 / 0.6 * 24 = 14.4w,也就是说一个进程一天可以跑14.4w数据,20个进程并发就可以达到288w。
在去掉了WAIT UP TO 2 SECONDS后的第二天,我们的重点转向了并发情况下的性能测试,之所以要测试,是因为简单的将14.4 * 20计算是不合理的,因为并发将导致进程之间竞争资源,从而使得单个进程效率下降,但是下降多少,我们并不知道。我们的开发机是一台4C 8核的1.6 GHz的P520,测试数据如下:
- 1进程*100条数据 47s
- 4进程*100条数据 65s
从上面的数据可以看到,4进程并发,虽然单位实现效率上升约190%,但是单个进程效率下降近38.3%,这让我们很担心,40个进程并行的时候但个进程性能会下降多少?是否还能达到总效率提升的效果呢?由于开发机只设置了4个后台进程,因此只能测试到4进程并发,我们非常期待在生产机测试。
除了并发可能导致效率下降,还必须考虑由于一次导入的数据量增加,会否使程序的效率恶化,虽然在此时我们还没有预见到程序中有一个致命的问题,但是这种考虑显然帮了我们大忙。我们决定在第二天晚上,跑一个4w条数据的量,看看执行效率如何,该进程在这一天晚上午夜12点启动了,同时也希望能测试一下服务器负载较低时,数据导入的效率能否更好一些。
4. 第三天
一大早我们期望看到结果,然而等待我们的却是远远超出预期,而且还未结束的进程。这让我们不得不担心程序中存在随数据量增长以指数方式恶化的情况。
通过调试程序,我们发现程序中存在一段两重循环外加一个READ TABLE,也就是检索。正是这个两重循环导致这段逻辑成2次方指数恶化,而在此循环内部的READ TABLE命令,也将本已恶化的性能推向了崩溃的边缘。在一个GENERIC TABLE中做READ TABLE操作,平均命中率是一半,相当于半个循环。
虽然循环中基本都是ABAP命令,极少有数据库读取,但是数据量的增大仍然导致了不可接受的后果,让我们来简单计算一下:
- 100条数据*50个字段 = 500,这个500就是本段循环的基础值,两重循环将使内部的循环次数达到500*500 = 25w
- 4w条数据*50个字段 = 200w,两重循环将使内部的循环次数达到200w * 200w = 4亿
- 500条数据的READ TABLE的平均查询次数为250次
- 200w条数据的READ TABLE的平均查询次数则为100w次
从上面的数据不难看出,4w/100 = 400倍的数据量,而循环次数则提高了1600倍,read table的查询次数也提高了4000倍,仅read table一项的查询次数就比原来增加了640w倍,这就是三次方指数的恶化。
经过进一步的分析,我们认为,原计划4小时左右跑完的进程,即使是跑了9个小时,也还没有完成这一小段逻辑。而在整体运行过程中,这一小段逻辑,还占不到整个程序消耗时间的1/100。早先的测试都是重点在于功能,由于最大测试数据不超过20条,虽然比1条数据扩大了8000倍的运行时间,但由于绝对占比不到1/100,根本没能发现。
好在最后我们将这个问题解决了,将三重循环改为顺序处理,说到这里,很多次处理数据的时候,在效率方面,我都从大学时代的编译原理课程受益匪浅。
在解决了这个问题之后,程序的复杂度终于随数据量增大符合线性增长了,早上9点40解决了问题之后,我们启动了同一个4w数据的进程,结果在不到4个小时的时间顺利运行完,这一次测试,我们得到的数据是0.3s/条的导入速度。
这一天下午,生产机准备好了,我们将全部精力投入在在生产环境的测试上。由于生产系统的配置尚无法支持业务数据的导入,因此我们想到了一个办法,首先对数据库进行压力测试,测试方法为复制一个MARA表,然后对其进行大数据量写入,然后对大并发进行测试。请看下面的测试结果,顺便提一下,生产机应用程序服务器共5台P595做负载均衡2台16C 32核 128G内存,3台8C 16核 64G内存,主频都是2.2GHz ,数据库服务器是一台P595,16C 32核 2.2GHz主频 128G内存:
- 1w条数据 * 1进程,开发机19s,生产机14s
- 1w条数据 * 4进程,开发机平均22.5s
- 1w条数据 * 10进程,生产机平均20s
- 1w条数据 * 20进程,生产机平均31.45s
- 10w条数据 * 20进程,生产机平均399s
从上面的测试数据看,生产机比开发机性能好,但是在生产机10进程并发和20进程并发分别下降42.8%和124.6%,10w条数据的写入时间换算后,进一步衰减177.7%,可见并发时的衰减还是很大的,并且也没能达到数量级的提升,这主要的原因是影响单个进程运行速度的主要因素是CPU速度、内存和磁盘速度,而生产机比开发机在单一性能上并没有强多少,因此生产机应该能在提供更大的吞吐量,我们猜测大量的并发可以提高总体效率。由于以上测试使用的程序几乎99%时间消耗在数据库上,因此我们推测,实际业务数据导入的时候,衰减会好一些。数据量加大后,同样是20进程,衰减再进一步扩大,这一点我们没有很好的解释,但依据次数据,我们推测导入业务数据时20进程的衰减应该在三者平均值约114%左右。
这一天,我们再次干到快午夜才离开单位,明天生产系统中会准备好业务数据供我们进行实际测试,希望接下来一天的测试能给我们一些好消息。
5. 第四天
我们将程序改好后传输到生产系统,由于先前我们认为BDC仍然是影响速度的一个关键因素,最终我们通过沟通决定暂时不导入最后一这部分,这让我们的导入提升接近一倍。我们对100条数据、500条数据做了单进程测试,可以达到0.125秒/条的速度。在晚上下班后,我们又做了500条数据*10进程并发的测试,单进程速度衰减100%,比我们预测的要好一些,接下来再做200条*40进程并发测试,单进程速度衰减仍然是100%,这让我们看到了服务器高吞吐的能力,也让心里有了底。
这一天还有BASIS的两位同事帮我们在做测试的时候监控系统后台资源使用状况,我们曾考虑到一些可能影响性能的参数,经过几次调整,虽然没有大幅提升,但是让系统资源得到了充分的利用。我们尝试了关闭Oracle redolog,尝试了Oracle服务器的进程设置等等。
按照0.125/条速度,30进程并发,速度下降100%计算,一天24小时系统可以接受1036w的数据导入,即使是考虑到去掉的BDC功能再跑一次,也可以接收超过500w的数据,目标完成。接下来就是完善程序的功能,包括修改输出日志、增加可调参数等等。在这一切都完毕之后,我们遇到了新的问题,数据。
以第一个导入数据的子项目来说,6w主数据扩展到96个工厂,总共是576w条,如果按照4w/文件计算,需要创建144个文件,可是事实上将来只有一两个人会做这项工作,如何能在一天之内准备144个数据文件、定义后台作业成了难题,因此我们决定扩大单个文件的数据量,提高到19.2万条数据/文件,这样刚好是30个文件,30个进程,每个进程将运行14个小时左右,时间刚刚好,30个进程并发也刚好使我们的预期。
这里我们还在担心一些问题,19.2w数据的excel文件将达到200M,需要消耗内存超过300M,保存一次需要近1分钟,保存的文件约74M,转换有的txt文件约60M,对19.2w的数据进行操作很有可能因为资源不足无法Ctrl+Z,这必然给数据整理工作造成及其大的障碍,对于这一问题,我们暂时不考虑了,从技术的角度讲,系统已经达到1036w数据/天的导入速度,而剩下的则是人的工作了。
5. 第五天
这一天,我的大部分时间用来写这篇文章,像东总说的,这是一次非常有价值的Performance Tuning工作,虽然我们只是对程序做了少量的修改和调整,但这一切都是基于4天没日没夜的测试、评估与计算上,没有这四天一次一次的测试数据,我们也无从知道大数据量、大量并发的情况下系统将如何表现。
总的来说这次性能调优还是收获很大,当然除了本文总结的内容,还有很多值得总结的地方,今天时间太紧张,日后如有可能再补上吧。这很有可能是我此次暂离公司之前做的最后一件事情了,按计划,下周回来,我要去找人力经理办理离职手续了,在临近离开之前,做这样一件事,好像是老天安排我一个收官之作,希望一切顺利,一切安好。
在此,我想感谢在这次工作中一起奋斗ABAP兄弟,Allan Zhu和Jamey Qi,还有BASIS洪老大、小熊,没有你们我做不好这事,也做不了,谢谢!
截止到现在,我已经结束了本周的工作,即将启程去上海,宝贝已经在上海等我了。
ps. 本周只做了这一件事情,所以只写了一篇文章。
