java大文件导入_java导入大文件数据的解决方案
2018年11月5号于南昌 中海蓝域小区 卧榻伴音弦
最近在做项目,一个20G大小的文件,要按行读入到数据库,妈呀,有什么好方法吗?
20G如果按照行读入的方式,需要20多个小时才能入库成功。主要性能瓶颈不是在内存,而是在数据库连接的次数。
比如批量单次插入1000条和单次插入200条,其实是五倍性能差距。
但是这个批量插入的条数是根据数据库缓存大小设置而决定的,数据库缓存不可能太大,比如一个8G的服务器,数据库缓存建议最大200M等,单次插入条数最大上限是2000条每次,如果有5000w行数据,那么需要多少次数据连接呢?答案是:2.5万次连接,我天,可想而知,能不慢吗?连接数据库,每次都有通过网络访问,然后数据库有行级锁,再提交事务,单次连接如果1s,那么2.5万次连接就是
2.5万s,一小时是3600s,那么需要多少个小时呢
结果:2.5万除以0.36万,大概是7个小时。
大家知道了,上面并没有考虑读文件,放入内存的时间,算是这段时间,
时间就会更长,为什么忽略这个,因为在我看来,数据库连接所消耗的时间要远大于内存加载文件中的数据所消耗的时间。
分析问题要切中要点,忽略次要的因素。
大家脑海中可能想过以下几个方法:
原始方法如下:
@Override
public void addDataFromBigDatFileOfScReturnRegister(String fileEndDate) throws IOException {
String currentDate = fileEndDate;// DateUtils.formatDate(new Date(),
// "yyyyMMdd");
String dataFileFlagName = LoyParam.DOWN_LOCAL_PATH_BEFORE.concat(currentDate)
.concat(LoyParam.DOWN_LOCAL_PATH_AFTER).concat(LoyParam.FILE_CIR_RETURN_REGISTER_).concat(currentDate)
.concat(LoyParam.DOWN_FILENAME_FLG);
String dataFileName = LoyParam.DOWN_LOCAL_PATH_BEFORE.concat(currentDate).concat(LoyParam.DOWN_LOCAL_PATH_AFTER)
.concat(LoyParam.FILE_CIR_RETURN_REGISTER_).concat(currentDate).concat(LoyParam.DOWN_FILENAME_DAT);
boolean flag_flg = FileUtil.isExistence(dataFileFlagName);
boolean flag_dat = FileUtil.isExistence(dataFileName);
if (!(flag_flg && flag_dat)) {
// 其中一个不存在over
return;
}
Map fileMap = this.getDataFlagInfo(dataFileFlagName);
if (fileMap.isEmpty()) {
logger.info("要访问的文件不存在" + dataFileFlagName);
return;
}
int datasize = 0;
if (!StringCommonUtils.isEmpty(fileMap.get("dataSum"))) {
datasize = Integer.parseInt(fileMap.get("dataSum"));
logger.info("数据文件名为:" + fileMap.get("dataFile"));
logger.info("数据文件大小为:" + fileMap.get("fileSize"));
logger.info("数据总数为:" + datasize);
}
// 年月日时分秒
String starttime = formatter.format(new Date());
// 10位的年月日
String currentY_M_D = formatterY_M_D.format(new Date());
long start = System.currentTimeMillis();
FileInputStream fis = new FileInputStream(dataFileName);
Scanner sc = new Scanner(fis, "GBK");
//创建备份表,插入备份表
this.deleteAndCreateTmpTable(LoyParam.TABLE_SC_RETURN_REGISTER);
int DB_COMMIT_COUNT=1000;
int y = datasize % DB_COMMIT_COUNT;
int inum = 0;
List ststlist = new ArrayList();
ScProblemRecordPojo problemPojo=null;
List problemList=new ArrayList();
ScReturnRegister scReturnRegister=null;
while (sc.hasNextLine()) {
String line = sc.nextLine();
String[] data = line.split("\\x03");
if (data.length > 0) {
inum++;
// logger.info("第"+inum+"行:"+line);
Map datamap = new HashMap();
for (int i = 0; i < data.length; i++) {
String data_ = StringCommonUtils.replaceBlank(data[i]);
datamap.put(i, data_);
}
try{
String dataDate = StringFormat.trimNull(datamap.get(0));
String loanNo = StringFormat.trimNull(datamap.get(1));
String orgNo = StringFormat.trimNull(datamap.get(2));
String custNo = StringFormat.trimNull(datamap.get(3));
String custName = StringFormat.trimNull(datamap.get(4));
String tranType = StringFormat.trimNull(datamap.get(5));
String returnInt = StringFormat.trimNull(datamap.get(6));
String returnAmt = StringFormat.trimNull(datamap.get(7));
String tbdate = starttime.substring(0, 10);
scReturnRegister = new ScReturnRegister();
BeanUtil.setProperty(scReturnRegister, "dataDate", dataDate);
BeanUtil.setProperty(scReturnRegister, "loanNo", loanNo);
BeanUtil.setProperty(scReturnRegister, "orgNo", orgNo);
BeanUtil.setProperty(scReturnRegister, "custNo", custNo);
BeanUtil.setProperty(scReturnRegister, "custName", custName);
BeanUtil.setProperty(scReturnRegister, "tranType", tranType);
BeanUtil.setProperty(scReturnRegister, "returnInt", returnInt);
BeanUtil.setProperty(scReturnRegister, "returnAmt", returnAmt);
BeanUtil.setProperty(scReturnRegister, "tbdate", tbdate);
}catch(Exception ex){
logger.error("转换信息异常"+ex.getMessage());
logger.info("========》我出错了 "+inum);
problemPojo=new ScProblemRecordPojo(
scReturnRegister==null?"":scReturnRegister.toString(),
DateUtils.YYYYMMDD,
DateUtils.YMDHMS,
dataFileName,
((Integer)inum).toString()
);
problemList.add(problemPojo);
this.scCredTmTxnHstService.insertBatch(problemList, dataFileName);
continue;
}
ststlist.add(scReturnRegister);
datamap.clear();
if (ststlist.size() == DB_COMMIT_COUNT) {
scCredTmTxnHstService.insertBatch(ststlist,dataFileName);
ststlist = null;
ststlist = new ArrayList();
continue;
}
if (y > 0 && inum == datasize) {
scCredTmTxnHstService.insertBatch(ststlist,dataFileName);
break;
}
}
}
logger.info("解析数据完成!共计" + inum + "条数据");
sc.close();
fis.close();
long end = System.currentTimeMillis();
System.out.println("时长为:---------------" + (start - end) + "毫秒---------------");
if (inum == datasize) {
FileOperate.delete(dataFileName);
scEngineeEtlmxService.addScEngineeEtlmx(LoyParam.FILE_CIR_RETURN_REGISTER_, "数仓数据文件之"+dataFileName, starttime,
formatter.format(new Date()), StringFormat.msecondToTime(end - start), currentY_M_D, datasize, inum,
currentY_M_D, "Y");
//如果成功做一系列处理
this.insertDBSuccessProcessTable(LoyParam.TABLE_SC_RETURN_REGISTER);
}
}
代码很好理解,这里不多解释,主要性能问题大家也知道
scCredTmTxnHstService.insertBatch(ststlist,dataFileName);
对,就在这里,将list数据,连接数据库,插入,提交事务,这个过程需要2.5万次,我天
考虑java解决问题的方式
1,多线程
多线程就是,将串行的方式,改成并行,比如:
排队去食堂打饭,就一个窗口,这是串行
排队去食堂打饭,但是有多个窗口共同执行,你可以在任何一个窗口排队,并行。
并行增加了效率,提高了打饭速度,随之而来的问题也很多。那就是并发问题。A人付款了,准备拿饭缸接住饭,这时候,由于并发,B人的没有付款,但是售货员把饭打到了B人的饭缸,造成了A付了款而没有收到饭,或者A可能收到了C人的饭,C收到了D的饭,乱套了。
解决这个方法,java当然也有解决方案,呵呵。
Synchronized神方法,不错,但是有一个问题,我们要对非原子的逻辑都要加Synchronized,所以方法块,或者干脆加到方法上。
言归正传,拥多线程并不能解决我们的问题,因为我们这个读取的方法,就这么一个,里面大多数都是非原子的逻辑,加载方法上,不管你启多少个线程,效果和串行一样,因为只有一个线程获取了锁,并且执行,而且速度也不快,所以这个方法解决不了问题
2,多进程
这个方法咋拥,其实就是把上面的方法,比如A 我们复制多个,分别叫
A A1 A2 A3
这里就有了4个方法,但是不能同时读取一个文件吧,这样数据会重复,有人会说数据库强加唯一索引,我说如果数据杂乱无章,没有主键呢?
这样就考虑将大文件切割,切割拥linux的split命令,见我下一篇文章,会有详细的解释
比如我们把文件F切割成 F1 F2 F3 F4
然后A方法读F1 入库
A1 读F2入库
A2 读F3入库
A3 读F4入库
这样我们可以启动4个进程,代码虽然缀余,但这不是我们讨论的重点,我们是讨论如何解决之,那么我们发现,这样的话,效率会增大,但是不会太明显,为什么,此时虽然说2.5万次连接,分了4个进程去连接,一个进程去执行2.5w除以4次连接,同时进行,相当于多线程,但他是多线程,
但为什么效果不明显,原因是数据库有行级锁,虽然同时连接的效率高了,但是对于数据库来说,处理插入是按照队列来的,是吧。
这样瓶颈就在数据库的行级锁,那么此时就没有办法了。
3 Channel 读取文件
这种方法,是java提供的读取同一个文件入库的多线程方法,大家有兴趣可以看一下,但是我没研究过,其实性能不在内存,而在数据库连接和数据库行级锁上面。
以上就是java导入大文件的解决方案,实际工作中的经验,分享之,愿大神有高见,指点一二,不胜感激。
======================
V。18612372242
励志语:
愿你我不忘初心,选一行爱一行,就像爱一个人终老一样
You are My love ForEver !
java大文件导入_java导入大文件数据的解决方案相关推荐
- java 读取大文件内容_java读取大文件
java一般读取文件时,将文件文内容全部加在到内存,然后读取,但是这种读取方式很明显不适合读取大文件,在进行大文件处理时,考虑到内存有限,采用分次读取的方式. java分次读取文件内容有三种方式, 1 ...
- java word 题目导入_Java导入导出Excel和Word
目的 实现Excel/Word导入导出,分以下步骤 导入 上传文件 解析Excel/Word里面的内容 导出 生成Excel/Word文件 文件下载 如何实现 上传文件----------Common ...
- java多线程 文件夹_Java多线程遍历文件夹,广度遍历加多线程加深度遍历结合
复习IO操作,突然想写一个小工具,统计一下电脑里面的Java代码量还有注释率,最开始随手写了一个递归算法,遍历文件夹,比较简单,而且代码层次清晰,相对易于理解,代码如下:(完整代码贴在最后面,前面是功 ...
- java实现视频文件转换为flv(带文件缩略图)_java实现视频文件转换为flv(带文件缩略图)...
在android中获取视频文件的缩略图有三种方法: 1.从媒体库中查询 2. android 2.2以后使用ThumbnailUtils类获取 3.调用jni文件,实现MediaMetadataRet ...
- java读取修改文件内容_Java对本地文件内容读取、修改、删除的操作
import org.apache.commons.lang.StringUtils; import java.io.*; import java.util.HashMap; import java. ...
- java编写程序实现文件拷贝_Java IO编程——文件拷贝
在操作系统里面有一个copy命令,这个命令的主要功能是可以实现文件的拷贝处理,现在要求模拟这个命令,通过初始化参数输入拷贝的源文件路径与拷贝的目标路径实现文件的拷贝处理. 需求分析: ·需要实现文件的 ...
- java文件递归_java递归处理文件夹和文件
import java.io.File; /** * 文件综合使用示例 */ public class FileDelete { public static void main(String[] ar ...
- java 获取文件权限_Java中的文件权限,检查权限和更改权限 - Break易站
Java提供了许多方法调用来检查和更改文件的权限,例如可以将只读文件更改为具有写入权限.当用户想要限制文件允许的操作时,需要更改文件权限.例如,文件权限可以从写入更改为只读,因为用户不再想要编辑文件. ...
- java 文件 递归_JAVA实现遍历文件夹下的所有文件(递归调用和非递归调用)
JAVA 遍历文件夹下的所有文件(递归调用和非递归调用) 1.不使用递归的方法调用. public void traverseFolder1(String path) { int fileNum = ...
- java 删除指定目录_Java 删除目录 指定文件
package delete; import java.io.File; /** * 删除文件操作 * @author zengpan * */ public class DeleteFileTest ...
最新文章
- 炸裂!MySQL 82 张图带你飞!
- Android程序如何实现从网络中获取一张图片
- string.format大全
- 扩展GridView控件——为内容项添加拖放及分组功能
- iOS-最全的App上架教程
- 神经网络为什么需要激活函数
- ajax与DOM的使用,AJAX和DOM的运行经验
- TPO-22 C1 Complain about a biased article
- osea/Beat Classification 4.3-4.5
- SSR pac模式配置和Gitee转移图床失败的尝试
- 360隐私保险箱 vs misuo
- Failed to introspect Class FeignClientFactoryBean 异常排查
- IE8跳转谷歌浏览器亲测有效
- ios实时卡顿检测和优化方案
- Lect5_Model_free_Control
- 和大于等于target的最短子数组 | 循序递进---@二十一画
- 方差分析 可汗学院统计学 笔记
- Node.js 学习(持续更新)
- Android 表情功能的完整处理方案
- [转帖]房博士教你购房(五)
热门文章
- 计算机桌面关不了机怎么办,电脑关不了机怎么办?
- 【软考备战·希赛网每日一练】2023年4月26日
- 词霸天下---126~128 词根【仅供学习使用】
- brackets的php插件,Web 设计与开发必备工具:Brackets 编辑器详细使用教程
- el-table 树形表格 自定义展开图标_换图标icon APP下载-换图标icon APP官方版 v3.01.0927...
- Glint360K | 使用指南
- html dom 替换节点,XML DOM替换节点
- 五个课时,百余智慧安防AI应用作品落地!AidLux实战训练营圆满收官
- bes2300之RTX介绍及简单运用(五)
- 实现本地主机与阿里云服务器的UDP通信