最近有个需求解析一个订单文件,并且说明文件可达到千万条数据,每条数据大概在20个字段左右,每个字段使用逗号分隔,需要尽量在半小时内入库。

思路

1.估算文件大小

因为告诉文件有千万条,同时每条记录大概在20个字段左右,所以可以大致估算一下整个订单文件的大小,方法也很简单使用FileWriter往文件中插入一千万条数据,查看文件大小,经测试大概在1.5G左右;

2.如何批量插入

由上可知文件比较大,一次性读取内存肯定不行,方法是每次从当前订单文件中截取一部分数据,然后进行批量插入,如何批次插入可以使用insert(...)values(...),(...)的方式,经测试这种方式效率还是挺高的;怎么快速插入 100 条数据,用时最短,这篇看下。

3.数据的完整性

截取数据的时候需要注意,需要保证数据的完整性,每条记录最后都是一个换行符,需要根据这个标识保证每次截取都是整条数,不要出现半条数据这种情况;

4.数据库是否支持批次数据

因为需要进行批次数据的插入,数据库是否支持大量数据写入,比如这边使用的mysql,可以通过设置max_allowed_packet来保证批次提交的数据量;

5.中途出错的情况

因为是大文件解析,如果中途出现错误,比如数据刚好插入到900w的时候,数据库连接失败,这种情况不可能重新来插一遍,所有需要记录每次插入数据的位置,并且需要保证和批次插入的数据在同一个事务中,这样恢复之后可以从记录的位置开始继续插入。

实现

1.准备数据表

这里需要准备两张表分别是:订单状态位置信息表,订单表;

CREATE TABLE `file_analysis` (

`id` bigint(20) NOT NULL AUTO_INCREMENT,

`file_type` varchar(255) NOT NULL COMMENT '文件类型 01:类型1,02:类型2',

`file_name` varchar(255) NOT NULL COMMENT '文件名称',

`file_path` varchar(255) NOT NULL COMMENT '文件路径',

`status` varchar(255) NOT NULL COMMENT '文件状态 0初始化;1成功;2失败:3处理中',

`position` bigint(20) NOT NULL COMMENT '上一次处理完成的位置',

`crt_time` datetime NOT NULL COMMENT '创建时间',

`upd_time` datetime NOT NULL COMMENT '更新时间',

PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8

CREATE TABLE `file_order` (

`id` bigint(20) NOT NULL AUTO_INCREMENT,

`file_id` bigint(20) DEFAULT NULL,

`field1` varchar(255) DEFAULT NULL,

`field2` varchar(255) DEFAULT NULL,

`field3` varchar(255) DEFAULT NULL,

`field4` varchar(255) DEFAULT NULL,

`field5` varchar(255) DEFAULT NULL,

`field6` varchar(255) DEFAULT NULL,

`field7` varchar(255) DEFAULT NULL,

`field8` varchar(255) DEFAULT NULL,

`field9` varchar(255) DEFAULT NULL,

`field10` varchar(255) DEFAULT NULL,

`field11` varchar(255) DEFAULT NULL,

`field12` varchar(255) DEFAULT NULL,

`field13` varchar(255) DEFAULT NULL,

`field14` varchar(255) DEFAULT NULL,

`field15` varchar(255) DEFAULT NULL,

`field16` varchar(255) DEFAULT NULL,

`field17` varchar(255) DEFAULT NULL,

`field18` varchar(255) DEFAULT NULL,

`crt_time` datetime NOT NULL COMMENT '创建时间',

`upd_time` datetime NOT NULL COMMENT '更新时间',

PRIMARY KEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=10000024 DEFAULT CHARSET=utf8

2.配置数据库包大小

mysql> show VARIABLES like '%max_allowed_packet%';

+--------------------------+------------+

| Variable_name | Value |

+--------------------------+------------+

| max_allowed_packet | 1048576 |

| slave_max_allowed_packet | 1073741824 |

+--------------------------+------------+

2 rows in set

mysql> set global max_allowed_packet = 1024*1024*10;

Query OK, 0 rows affected

通过设置max_allowed_packet,保证数据库能够接收批次插入的数据包大小;不然会出现如下错误:

Caused by: com.mysql.jdbc.PacketTooBigException: Packet for query is too large (4980577 > 1048576). You can change this value on the server by setting the max_allowed_packet' variable.

at com.mysql.jdbc.MysqlIO.send(MysqlIO.java:3915)

at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2598)

at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2778)

at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2834)

3.准备测试数据

public static void main(String[] args) throws IOException {

FileWriter out = new FileWriter(new File("D://xxxxxxx//orders.txt"));

for (int i = 0; i

out.write(

"vaule1,vaule2,vaule3,vaule4,vaule5,vaule6,vaule7,vaule8,vaule9,vaule10,vaule11,vaule12,vaule13,vaule14,vaule15,vaule16,vaule17,vaule18");

out.write(System.getProperty("line.separator"));

}

out.close();

}

使用FileWriter遍历往一个文件里插入1000w条数据即可,这个速度还是很快的,不要忘了在每条数据的后面添加换行符(\n\r);

4.截取数据的完整性

除了需要设置每次读取文件的大小,同时还需要设置一个参数,用来每次获取一小部分数据,从这小部分数据中获取换行符(\n\r),如果获取不到一直累加直接获取为止,这个值设置大小大致同每条数据的大小差不多合适,部分实现如下:

ByteBuffer byteBuffer = ByteBuffer.allocate(buffSize); // 申请一个缓存区

long endPosition = batchFileSize + startPosition - buffSize;// 子文件结束位置

long startTime, endTime;

for (int i = 0; i < count; i++) {

startTime = System.currentTimeMillis();

if (i + 1 != count) {

int read = inputChannel.read(byteBuffer, endPosition);// 读取数据

readW: while (read != -1) {

byteBuffer.flip();// 切换读模式

byte[] array = byteBuffer.array();

for (int j = 0; j

byte b = array[j];

if (b == 10 || b == 13) { // 判断\n\r

endPosition += j;

break readW;

}

}

endPosition += buffSize;

byteBuffer.clear(); // 重置缓存块指针

read = inputChannel.read(byteBuffer, endPosition);

}

} else {

endPosition = fileSize; // 最后一个文件直接指向文件末尾

}

...省略,更多可以查看Github完整代码...

}

如上代码所示开辟了一个缓冲区,根据每行数据大小来定大概在200字节左右,然后通过遍历查找换行符(\n\r),找到以后将当前的位置加到之前的结束位置上,保证了数据的完整性;

5.批次插入数据

通过insert(...)values(...),(...)的方式批次插入数据,部分代码如下:

// 保存订单和解析位置保证在一个事务中

SqlSession session = sqlSessionFactory.openSession();

try {

long startTime = System.currentTimeMillis();

FielAnalysisMapper fielAnalysisMapper = session.getMapper(FielAnalysisMapper.class);

FileOrderMapper fileOrderMapper = session.getMapper(FileOrderMapper.class);

fileOrderMapper.batchInsert(orderList);

// 更新上次解析到的位置,同时指定更新时间

fileAnalysis.setPosition(endPosition + 1);

fileAnalysis.setStatus("3");

fileAnalysis.setUpdTime(new Date());

fielAnalysisMapper.updateFileAnalysis(fileAnalysis);

session.commit();

long endTime = System.currentTimeMillis();

System.out.println("===插入数据花费:" + (endTime - startTime) + "ms===");

} catch (Exception e) {

session.rollback();

} finally {

session.close();

}

...省略,更多可以查看Github完整代码...

如上代码在一个事务中同时保存批次订单数据和文件解析位置信息,batchInsert通过使用mybatis的标签来遍历订单列表,生成values数据;

总结

以上展示了部分代码,完整的代码可以查看Github地址中的batchInsert模块,本地设置每次截取的文件大小为2M。

经测试1000w条数据(大小1.5G左右)插入mysql数据库中,大概花费时间在20分钟左右,当然可以通过设置截取的文件大小,花费的时间也会相应的改变。

推荐去我的博客:

觉得不错,别忘了点赞+转发哦!

mysql如何快速插入一千万条数据_如何快速安全的插入千万条数据?相关推荐

  1. java mysql 快速插入1000w条数据_教你88秒插入1000万条数据到mysql数据库表

    我用到的数据库为,mysql数据库5.7版本的 1.首先自己准备好数据库表 其实我在插入1000万条数据的时候遇到了一些问题,现在先来解决他们,一开始我插入100万条数据时候报错,控制台的信息如下: ...

  2. mysql找出两个结果集不同数据_如何快速对比MySQL两个不同实例上的数据并找出差异...

    在MySQL运维中,研发同事想对比下两个不同实例上的数据并找出差异,除主键外还需要对比每一个字段,如何做呢? 第一种方案,写程序将两个实例上的每一行数据取出来进行对比,理论可行,但是对比时间较长. 第 ...

  3. 只导表前10条数据_【205期】面试官:数据量很大的情况下,对于分页查询你有什么优化方案吗?...

    点击上方"Java面试题精选",关注公众号 面试刷图,查缺补漏 >>号外:公众号改版后文章顺序不固定,欢迎大家把我们面试题精选这个公众号设置为星标,感谢大家一年的支持! ...

  4. 从hbitmap中获取位图数据_如何快速从主流数据库中获取人/小鼠数据?

    点击上方"蓝色字体"关注我们 鹿明 生物 蛋白.代谢组学服务专家 关注我们收获更多 关注 随着生物科技的迅速发展,每天都会有海量的生物学数据产生,如何有效的分析这些"生物 ...

  5. 服务器管理器正在收集清单数据_数据仓库快速入门教程1简介

    数据仓库是从各种渠道收集和管理数据的技术,可提供有意义的业务洞察,战略性地使用数据. 它用于查询和分析而不是事务处理,是将数据转换为信息并及时向用户提供的过程. 决策支持数据库(数据仓库)与组织的运营 ...

  6. 查询时拼接两列数据_如何用VBA代码查询两列数据差异?

    爱就一个字,我只说一次-- 北京市第三交通委提醒您:代码千万条,注释第一条,命名不规范,修订两行泪-- 咳,给大家拜晚年了,再提前祝大家元宵快乐-- 我们今天和大家分享的内容是如何用VBA代码查询两列 ...

  7. 怎样用excel剔除异常数据_(如何剔除excel表格中重复的数据)excel表格怎么剔除异常数据...

    请问 Excel 如何剔除与平均数偏差较大的数字再求平均数?谢谢. 把问题作为内容(邮件主定要包含"excel",本人以此据辨别非垃圾邮件,以免误删).excel样件(请特别注意: ...

  8. layui表格更改一列数据_【WEB前端开辟】layui表格数据变动的一种处理方式

    表格数据更改,平常包括几个内容:新增.修正.删除.挪动,开辟中常常会面对的一个题目就是更改以后怎样将数据同步到节点上,一向以来个人的提议照样应用表格重载,不管是url情势的照样data情势的实际都是须 ...

  9. asp后台调用产品数据_后台产品经理,需掌握这些数据交互知识

    人们每天都在接收信息和发送信息,在传递信息的过程中,明白对方要表达的意思.数据也是如此,在系统交换数据的过程中,就伴随着数据交互.本篇文章将为大家具体分析前端和后台的数据交互与协议. 本文所说的&qu ...

最新文章

  1. photoshop ps 钢笔工具抠出图 复制出来 方法
  2. python引用大数据,Python 3 UDF
  3. 西班牙电信拟出售 60亿欧元资产
  4. Spring Cloud Alibaba:Sentinel 流控规则
  5. Hive引擎改为Tez笔记
  6. MessagePack Java Jackson Dataformat 在 Map 中不使用 String 为 Key 的序列化
  7. android手机连接无线路由器上网设置,手机连接无线网络怎么设置?手机Wifi无线网设置教程...
  8. oc引导win方法_[OC更新]机械革命8代、9代标压稳定版更新
  9. 两台windows服务器----SVN的迁移
  10. cass读取dat文件_南方CASS教程+视频讲解+插件汇总,小白快速上手的测绘神器
  11. Jdbc -Statement
  12. mysql group by cube_SparkSQL 中group by、grouping sets、rollup和cube方法详解
  13. html5 video标签兼容性与自定义控件
  14. pl/sql 中变量定义范围的小例子:
  15. java 拉钩技术_拉钩JAVA高薪训练营笔记汇总
  16. linux内核命令行解析
  17. css3实现水平垂直居中
  18. 计算机毕设中期检查表怎么写,[毕业论文中期检查表(精选多篇)] 毕业论文中期检查表怎么写...
  19. IGS Viewer / IGS 文件查看器
  20. STC15单片机-PCB设计

热门文章

  1. 使用Ingress来负载分发微服务
  2. 一张图带你了解 Insider Dev Tour 2019中国技术大会
  3. HttpClientFactory系列二:集成Polly处理瞬态故障
  4. 读 《CSharp Coding Guidelines》有感
  5. ASP.NET CORE 微服务(简化版)实战系列-没有比这性价比再高的实战课程了
  6. Postgresql快速写入\/读取大量数据(.net)
  7. .NET Framework 4.7发布,支持Windows 10创作者更新
  8. Cactus在jexus上安装
  9. ASP.NET Core 中文文档 第一章 入门
  10. onlyoffice回调函数controller方式实现