MySQL优化之LOAD DATA LOCAL INFILE实现大批量插入
如今mysql普遍的插入方式有如下两种:
1、循环单条插入
- <insert id="insert" parameterType="com.chargeProject.consumer.entity.Test">
- insert into test (id, nums, name)
- values (#{id,jdbcType=INTEGER}, #{nums,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR})
- </insert>
2、拼装批量插入
- <insert id="batchInsert" parameterType="java.util.List">
- insert into test (id, nums, name)
- values
- <foreach collection="list" item="item" separator=",">
- (#{item.id,jdbcType=INTEGER}, #{item.nums,jdbcType=INTEGER}, #{item.name,jdbcType=VARCHAR})
- </foreach>
- </insert>
一般都是通过mybatis框架进行辅助实现的,当然也可以自动拼装。今天介绍的是mysql自带的一种批量插入方式且效率更高,通过LOAD DATA LOCAL INFILE实现大批量插入。
MySQL使用LOAD DATA LOCAL INFILE从文件中导入数据比insert语句要快,MySQL文档上说要快20倍左右。
但是这个方法有个缺点,就是导入数据之前,必须要有文件,也就是说从文件中导入。这样就需要去写文件,以及文件删除等维护。某些情况下,比如数据源并发的话,还会出现写文件并发问题,很难处理。
那么有没有什么办法,可以达到同样的效率,直接从内存(IO流中)中导入数据,而不需要写文件呢?
MySQL社区提供这样一个方法:setLocalInfileInputStream(),此方法位于com.mysql.jdbc.PreparedStatement 类中。通过使用 MySQL JDBC 的setLocalInfileInputStream 方法实现从Java InputStream中load data local infile 到MySQL数据库中。
代码如下:
- @Component
- public class LoadDataInFileUtil {
- private Logger logger = LoggerFactory.getLogger(LoadDataInFileUtil.class);
- private Connection conn = null;
- @Resource
- private JdbcTemplate jdbcTemplate;
- /*-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --*/
- /**
- * 将数据从输入流加载到MySQL。
- *
- * @param loadDataSql SQL语句。
- * @param dataStream 输入流。
- * @param jdbcTemplate JDBC。
- * @return int 成功插入的行数。
- */
- private int bulkLoadFromInputStream(String loadDataSql,
- InputStream dataStream,
- JdbcTemplate jdbcTemplate) throws SQLException {
- if (null == dataStream) {
- logger.info("输入流为NULL,没有数据导入。");
- return 0;
- }
- conn = jdbcTemplate.getDataSource().getConnection();
- PreparedStatement statement = conn.prepareStatement(loadDataSql);
- int result = 0;
- if (statement.isWrapperFor(com.mysql.jdbc.Statement.class)) {
- com.mysql.jdbc.PreparedStatement mysqlStatement = statement.unwrap(com.mysql.jdbc.PreparedStatement.class);
- mysqlStatement.setLocalInfileInputStream(dataStream);
- result = mysqlStatement.executeUpdate();
- }
- return result;
- }
- /*-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --*/
- /**
- * 组装 SQL 语句。
- *
- * @param dataBaseName 数据库名。
- * @param tableName 表名。
- * @param columnName 要插入数据的列名。
- */
- public String assembleSql(String dataBaseName, String tableName, String columnName[]) {
- String insertColumnName = StringUtils.join(columnName, ",");
- String sql = "LOAD DATA LOCAL INFILE 'sql.csv' IGNORE INTO TABLE " + dataBaseName + "." + tableName + "(" + insertColumnName + ")";
- return sql;
- }
- /*-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --*/
- /**
- * 往 StringBuilder 里追加数据。
- *
- * @param builder StringBuilder。
- * @param object 数据。
- */
- public void builderAppend(StringBuilder builder, Object object) {
- builder.append(object);
- builder.append("\t");
- }
- /*-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --*/
- /**
- * 往 StringBuilder 里追加一条数据的最后一个字段。
- *
- * @param builder StringBuilder。
- * @param object 数据。
- */
- public void builderEnd(StringBuilder builder, Object object) {
- builder.append(object);
- builder.append("\n");
- }
- /*-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --*/
- /**
- * 通过 LOAD DATA LOCAL INFILE 大批量导入数据到 MySQL。
- *
- * @param sql SQL语句。
- * @param builder 组装好的数据。
- */
- public int fastInsertData(String sql, StringBuilder builder) {
- int rows = 0;
- InputStream is = null;
- try {
- byte[] bytes = builder.toString().getBytes();
- if (bytes.length > 0) {
- is = new ByteArrayInputStream(bytes);
- //批量插入数据。
- long beginTime = System.currentTimeMillis();
- rows = bulkLoadFromInputStream(sql, is, jdbcTemplate);
- long endTime = System.currentTimeMillis();
- logger.info("LOAD DATA LOCAL INFILE :【插入" + rows + "行数据至MySql中,耗时" + (endTime - beginTime) + "ms。】");
- }
- } catch (SQLException e) {
- e.printStackTrace();
- } finally {
- try {
- if (null != is) {
- is.close();
- }
- if (null != conn) {
- conn.close();
- }
- } catch (IOException | SQLException e) {
- e.printStackTrace();
- }
- }
- return rows;
- }
- /*-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --*/
- }
调试代码如下:
- // 数据库名。
- public static final String DATA_BASE_NAME = "charge";
- // 表名。
- public static final String TABLE_NAME = "test";
- // 要插入数据的列名。(必须与插入的数据一一对应)
- public static final String COLUMN_NAME[] = {"id", "nums", "name"};
- @Override
- public ResultContent insert(String name) {
- StopWatch stopWatch = new StopWatch();
- stopWatch.start();
- StringBuilder sb = new StringBuilder();
- List<Test> list = new ArrayList<>();
- for(int i = 1; i < 100000; i++) {
- loadDataInFileUtil.builderAppend(sb, UUID.randomUUID().toString());
- loadDataInFileUtil.builderAppend(sb, i);
- loadDataInFileUtil.builderEnd(sb, name + i);
- }
- String sql = loadDataInFileUtil.assembleSql(DATA_BASE_NAME, TABLE_NAME, COLUMN_NAME);
- int insertRow = loadDataInFileUtil.fastInsertData(sql, sb);
- System.out.println("insert应收报表数量insertRow:"+insertRow);
- stopWatch.stop();
- System.out.println("花费时间" + stopWatch.getTotalTimeSeconds());
- System.out.println("---------方法执行结束--------------");
- return new ResultContent(0, "success", name);
- }
经过测试插入1w条数据时候与拼装批量插入语句时间差别不大,当插入数量达到10w出现了明显的时间差:
拼装批量插入语句花费时间:6.83s
LOAD DATA LOCAL INFILE实现大批量插入花费时间:1.23s
当表格的字段更多数据量更大出现的时间差就越大。
总结:当需要进行大批量数据插入的时候,可以优先考虑LOAD DATA LOCAL INFILE实现方式。
MySQL优化之LOAD DATA LOCAL INFILE实现大批量插入的更多相关文章
- 使用 LOAD DATA LOCAL INFILE,sysbench 导数速度提升30%
1. LOAD DATA INFILE 为什么比 INSERT 快? 2. sysbench 压测 MySQL 的四个标准步骤. 3. 怎么让 sysbench 支持 LOAD DATA LOCAL ...
- [MySQL]load data local infile向MySQL数据库中导入数据时,无法导入和字段不分离问题。
利用load data将文件中的数据导入数据库表中的时候,遇到了两个问题. 首先是load data命令无法执行的问题: 命令行下输入load data local infile "path ...
- MySQL使用LOAD DATA LOCAL INFILE报错
在windows系统的MySQL8.0中尝试执行以下语句时报错 mysql> LOAD DATA LOCAL INFILE '/path/filename' INTO TABLE tablena ...
- 浅谈MySQL load data local infile细节 -- 从源码层面
相信大伙对mysql的load data local infile并不陌生,今天来巩固一下这里面隐藏的一些细节,对于想自己动手开发一个mysql客户端有哪些点需要注意的呢? 首先,了解一下流程: 3个 ...
- MySQL用Load Data local infile 导入部分数据后中文乱码
今天在两台MySQL服务器之间导数据,因为另一个MySQL服务器是测试用的,差一个月的数据,从现有MySQL服务器select到一个文件,具体语句是: select * from news where ...
- Java不写文件,LOAD DATA LOCAL INFILE大批量导入数据到MySQL的实现(转)
MySQL使用load data local infile 从文件中导入数据比insert语句要快,MySQL文档上说要快20倍左右.但是这个方法有个缺点,就是导入数据之前,必须要有文件,也就是说从文 ...
- Load data local infile 实验报告
1.实验内容: 利用SQL语句“load data local infile”将“pet.txt”文本文件中的数据导入到mysql中 (pet表在数据库menagerie中) 2.实验过程及结果: ( ...
- load data local infile
发财 基本语法:load data [low_priority] [local] infile '文件名称' [replace替换策略 | ignore忽略策略]into table 表名称[fiel ...
- 合理使用mysql中的load data infile导入数据
基本语法: load data [low_priority] [local] infile 'file_name txt' [replace | ignore]into table tbl_name ...
- Mybatis拦截器 mysql load data local 内存流处理
Mybatis 拦截器不做解释了,用过的基本都知道,这里用load data local主要是应对大批量数据的处理,提高性能,也支持事务回滚,且不影响其他的DML操作,当然这个操作不要涉及到当前所lo ...
随机推荐
- JPA在SpringBoot中简单使用
前言 在SpringBoot项目中可以与JPA进行搭配,这样会省很多的开发时间,以下为JPA的简单使用 一.导入依赖 <!-- springboot jpa依赖--> <depend ...
- 记一次 .NET 某发证机系统 崩溃分析
一:背景 1. 讲故事 前些天有位朋友在微信上找到我,说他的系统有偶发崩溃,自己也没找到原因,让我帮忙看下怎么回事,我分析dump一直都是免费的,毕竟对这些东西挺感兴趣,有问题可以直接call我,好了 ...
- 非常'肤浅'的理解MVVM
那天领导给了我这么一个需求,就是他会通过接口给我传递一条数据,然后我需要判断这条数据的首字母是不是A,如果是的话,就把这条数据保存下来 很简单的一个需求对吧,直接开干,代码如下 完美的解决这个问题,所 ...
- HZOI NOIP 2024 Round 24 T2 取石子 官方做法
发现大多数的题解都是不同于官方题解的做法,这里我将介绍官方题解做法. Solution 证明先手是否可以必胜的方法相差无几,为了方便后边行文,这里介绍我的思路:考虑各堆石子和为奇数的情况(以下简称为& ...
- 从 TCP 到 WebSocket:一次搞懂网络通信的三层演进
引言 在现代应用开发中,网络通信是绕不开的核心议题.无论是构建传统的 Web 应用,还是开发需要实时交互的系统(如在线协作工具.金融行情推送.多人游戏),我们总会与 TCP.HTTP.WebSocke ...
- DotTrace系列:9. 大结局之 跨平台 和 自定义行为 诊断
一:背景 1. 讲故事 本篇是系列的最后一篇,我们从跨平台部署和自定义诊断的角度跟大家聊一聊 dottrace,希望对大家有所启发. 二:跨平台和自定义诊断 1. 如何跨平台诊断分析 如果 dottr ...
- Kafka入门实战教程(6):调优Kafka的实践
1 调优Kafka的目标 通常来说,任何系统调优的目标都是为了满足系统常见的非功能性需求,而性能则是众多非功能性需求中最重要的一个. 不同的系统对性能的侧重点不同,DB的话性能是响应时间,而对于Kaf ...
- org.springframework.amqp.AmqpConnectException: java.net.ConnectException: Connection refused: connec
(springcloud Stream整合rabbitmq消息驱动生产者踩坑)消息驱动之生产者8801 1.首先说一下情况,我是跟着尚硅谷周阳老师的springcloud2020教程学习的,前面也踩了 ...
- English-英语发音舌位唇形图
英语发音舌位唇形图 图0 梯形图 图1 图2 图3
- Win11系统电脑如何取消登录密码的问题
很多电脑基地的用户升级win11系统,但是电脑设置了密码之后,每次开机需要输入密码才能进入桌面.其实,我们设置密码是为了防止陌生人开机就可以进入桌面,但是自己每次进入桌面也要输入密码就很麻烦,下面技术 ...