海量数据解析并调接口的方案?
最近遇到了这样一个问题:
要求解析csv文件,逐行取出数据,并调用第三方指定的接口进行数据同步。
听上去很简单直白,甚至调用的demo都写好了,什么加密的逻辑都是现成的,只要把真实的秘钥、appid之类的信息填进去就能跑,感觉用个工具类解析csv文件,逐行拿出来作为入参传进去了事。 但拿到了数据文件才发现没这么简单,里面居然有上亿的数据...... 先取了部分数据同步地逐行组参调用试了试效果,发现接口性能比较差,单次调用都上1秒多。真要同步一行行来操作的话要搞到猴年马月去了~
又尝试了下多线程的方法。 不知道是我个人电脑性能太差,还是线程池配置的参数不理想,感觉效果并不好,甚至还不如同步的稳定性高。 跑了一段时间后,就没反应了,然后报java.lang.OutOfMemoryError: GC overhead limit exceeded 直接整挂了。
说实话这么大量的数据,要同步的话照理应该考虑数据库层面的同步方式吧。 不过现在受条件限制要求只能调人家的接口。 有没有什么高效一点的方法啊? 另外感觉这种大型工作还是最好在专门服务器上搞把,个人电脑吃不消~
回答:
这里面包含好几个问题:
- 单次调用1秒多,指的应该是从你发出请求到第三方接口回复需要1s吧,性能瓶颈不在你的解析数据文件,这种情况需要再看看第三方是否提供了批量处理接口。
- 多线程OOM,建议贴上代码提问,这是我们比较感兴趣希望能解答的部分。怀疑是线程中有内存泄露,资源没回收,或者线程池大小/队列没限制,直接开到了Integer.MAX_VALUE
- 异构数据库(从csv到数据库也算异构)同步本质上还是对数据库接口的调用,可以通过批量写入,优化数据库语句等方式改善性能,首先的建议是减少网络传输的时延,在最靠近数据库的地方处理,毕竟1s的时间里可能各层网络转发占90%,如果每次都是单独接口经过TLS握手,业务认证...无谓的时间消耗更多。
- 云厂商租一个服务器呗,2核到64核+,用完就关按需收费。
回答:
@Hotlink解答挺详细的了,我再补充一点OOM的问题。理论上,按楼主的问题,只是一个解析较大的CSV文件(上亿的数据),简单采用点策略,应该不至于到OOM的程度。
考虑一次性执行可能由于各种原因失败,而不得不重新执行,这里我们可以采取一点简单的策略,以便这部分逻辑随时可以中断、随时可以重新快速恢复执行。
- 关于CSV文件格式:我用Excel2013做了下试验导出CSV格式。目前看来,记录换行符为"\r\n",如果某一个单元格内部包含ALT+ENTER产生的换行,那么这个单元格内部换行使用的是"\n",即不包含"\r"字符。
- 快速建立CSV文件的记录索引。只需要扫描所有"\r\n"即可,可以快速建立记录索引和字节偏移的对应关系,从而也可以方便计算出每条记录的字节数。这一步通常很快,应该几秒可以完成。
- 使用RandomAccessFile来对CSV文件进行随机读取,利用seek()方法快速定位到需要的记录,使用read(byte b[], int off, int len)方法读取本条记录内容。这里一步非常关键,那就是复用字节数组,这个字节数组的长度设置为 最大记录字节数即可。
- 发送API调用请求,调用成功后,标识这条记录已经处理完成。下次重启时,可以通过处理标识快速跳过已处理的记录。
如果需要使用多线程处理API的调用,那么只需要发送API调用部分逻辑包装为一个Task,丢到一个任务队列中,由线程池来执行即可。这样可以方便调节执行的线程数。
需要注意的是,如果由线程池来调度执行,那么必须要注意避免多线程共享复用的字节数组,简单的可以把字节数组放入ThreadLocal中管理;如果介意ThreadLocal的性能损失的话,放到一个buf[threadId]数组中管理也很方便。
关于已处理记录的状态管理:对于这种需要频繁读写状态的数据文件,以我个人经验,使用MappedByteBuffer非常方便。相当于对文件的读写转换成了对内存的读写,速度相当快,而且安全。状态文件的设计,简单比如:每条记录占用 5 个字节(4字节偏移地址 + 1字节处理状态)。
以上是 海量数据解析并调接口的方案? 的全部内容, 来源链接: utcz.com/p/944404.html