Java实现大批量数据导入导出(100W以上) -(一)导入

这篇博客介绍了如何在Java中处理大批量数据导入,特别是超过100万条记录的情况。由于业务需求,不能直接使用SQL导入,因为需要数据校验和转换。博主提出了三个技术难点:内存溢出、网络传输压力和数据库压力,并给出了相应的解决方案:文件流上传、多线程分批读取和分批插入数据库。关键代码展示了如何使用FileChannel和ByteBuffer进行分批读取,以及如何监听读取并分批调用插入接口。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近业务方有一个需求,需要一次导入超过100万数据到系统数据库。可能大家首先会想,这么大的数据,干嘛通过程序去实现导入,为什么不直接通过SQL导入到数据库。

大数据量报表导出请参考:Java实现大批量数据导入导出(100W以上) -(二)导出

一、为什么一定要在代码实现

说说为什么不能通过SQL直接导入到数据库,而是通过程序实现:

1. 首先,这个导入功能开始提供页面导入,只是开始业务方保证的一次只有<3W的数据导入;

2. 其次,业务方导入的内容需要做校验,比如门店号,商品号等是否系统存在,需要程序校验;

3. 最后,业务方导入的都是编码,数据库中还要存入对应名称,方便后期查询,SQL导入也是无法实现的。

基于以上上三点,就无法直接通过SQL语句导入数据库。那就只能老老实实的想办法通过程序实现。

二、程序实现有以下技术难点

1. 一次读取这么大的数据量,肯定会导致服务器内存溢出;

2. 调用接口保存一次传输数据量太大,网络传输压力会很大;

3. 最终通过SQL一次批量插入,对数据库压力也比较大,如果业务同时操作这个表数据,很容易造成死锁。

三、解决思路

根据列举的技术难点我的解决思路是:

1. 既然一次读取整个导入文件,那就先将文件流上传到服务器磁盘,然后分批从磁盘读取(支持多线程读取),这样就防止内存溢出;

2. 调用插入数据库接口也是根据分批读取的内容进行调用;

3. 分批插入数据到数据库。

四、具体实现代码

1. 流式上传文件到服务器磁盘

 略,一般Java上传就可以实现,这里就不贴出。

2. 多线程分批从磁盘读取

批量读取文件:

  1 import org.slf4j.Logger;
  2 import org.slf4j.LoggerFactory;
  3 
  4 import java.io.File;
  5 import java.io.FileNotFoundException;
  6 import java.io.RandomAccessFile;
  7 import java.nio.ByteBuffer;
  8 import java.nio.channels.FileChannel;
  9 
 10 /**
 11  * 类功能描述:批量读取文件
 12  *
 13  * @author WangXueXing create at 19-3-14 下午6:47
 14  * @version 1.0.0
 15  */
 16 public class BatchReadFile {
 17     private final Logger LOGGER = LoggerFactory.getLogger(BatchReadFile.class);
 18     /**
 19      * 字符集UTF-8
 20      */
 21     public static final String CHARSET_UTF8 = "UTF-8";
 22     /**
 23      * 字符集GBK
 24      */
 25     public static final String CHARSET_GBK = "GBK";
 26     /**
 27      * 字符集gb2312
 28      */
 29     public static final String CHARSET_GB2312 = "gb2312";
 30     /**
 31      * 文件内容分割符-逗号
 32      */
 33     public static final String SEPARATOR_COMMA = ",";
 34 
 35     private int bufSize = 1024;
 36     // 换行符
 37     private byte key = "\n".getBytes()[0];
 38     // 当前行数
 39     private long lineNum = 0;
 40     // 文件编码,默认为gb2312
 41     private String encode = CHARSET_GB2312;
 42     // 具体业务逻辑监听器
 43     private ReaderFileListener readerListener;
 44 
 45     public void setEncode(String encode) {
 46         this.encode = encode;
 47     }
 48 
 49     public void setReaderListener(ReaderFileListener readerListener) {
 50         this.readerListener = readerListener;
 51     }
 52 
 53     /**
 54      * 获取准确开始位置
 55      * @param file
 56      * @param position
 57      * @return
 58      * @throws Exception
 59      */
 60     public long getStartNum(File file, long position) throws Exception {
 61         long startNum = position;
 62         FileChannel fcin = new RandomAccessFile(file, "r").getChannel();
 63         fcin.position(position);
 64         try {
 65             int cache = 1024;
 66             ByteBuffer rBuffer = ByteBuffer.allocate(cache);
 67             // 每次读取的内容
 68             byte[] bs = new byte[cache];
 69             // 缓存
 70             byte[] tempBs = new byte[0];
 71             while (fcin.read(rBuffer) != -1) {
 72                 int rSize = rBuffer.position();
 73                 rBuffer.rewind();
 74                 rBuffer.get(bs);
 75                 rBuffer.clear();
 76                 byte[] newStrByte = bs;
 77                 // 如果发现有上次未读完的缓存,则将它加到当前读取的内容前面
 78                 if (null != tempBs) {
 79                     int tL = tempBs.length;
 80                     newStrByte = new byte[rSize + tL];
 81                     System.arraycopy(tempBs, 0, newStrByte, 0, tL);
 82                     System.arraycopy(bs, 0, newStrByte, tL, rSize);
 83                 }
 84                 // 获取开始位置之后的第一个换行符
 85                 int endIndex = indexOf(newStrByte, 0);
 86                 if (endIndex != -1) {
 87                     return startNum + endIndex;
 88                 }
 89                 tempBs = substring(newStrByte, 0, newStrByte.length);
 90                 startNum += 1024;
 91             }
 92         } finally {
 93             fcin.close();
 94         }
 95         return position;
 96     }
 97 
 98     /**
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值