Java不写文件,LOAD DATA LOCAL INFILE大批量导入数据到MySQL的实现

欢迎关注我的公众号(无广告、不卖课,只分享技术干货)

在这里插入图片描述

 

大家都知道当插入大批量数据MySQL的时候,
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数据库中。

 

准备测试表 
SQL如下:

 

use test;
CREATE TABLE `test` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `a` int(11) NOT NULL,
  `b` bigint(20) unsigned NOT NULL,
  `c` bigint(20) unsigned NOT NULL,
  `d` int(10) unsigned NOT NULL,
  `e` int(10) unsigned NOT NULL,
  `f` int(10) unsigned NOT NULL,
  PRIMARY KEY (`id`),
  KEY `a_b` (`a`,`b`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8

 

 

 

 

 

 

 

Java代码如下:

 

 

 

package com.seven.dbTools.DBTools;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;

import org.apache.log4j.Logger;

/**
 * @author seven
 * @since 07.03.2013
 */
public class BulkLoadData2MySQL {

	private static final Logger logger = Logger.getLogger(BulkLoadData2MySQL.class);
	private JdbcTemplate jdbcTemplate;
	private Connection conn = null;

	public void setDataSource(DataSource dataSource) {
		this.jdbcTemplate = new JdbcTemplate(dataSource);
	}

	public static InputStream getTestDataInputStream() {
		StringBuilder builder = new StringBuilder();
		for (int i = 1; i <= 10; i++) {
			for (int j = 0; j <= 10000; j++) {

				builder.append(4);
				builder.append("\t");
				builder.append(4 + 1);
				builder.append("\t");
				builder.append(4 + 2);
				builder.append("\t");
				builder.append(4 + 3);
				builder.append("\t");
				builder.append(4 + 4);
				builder.append("\t");
				builder.append(4 + 5);
				builder.append("\n");
			}
		}
		byte[] bytes = builder.toString().getBytes();
		InputStream is = new ByteArrayInputStream(bytes);
		return is;
	}

	/**
	 * 
	 * load bulk data from InputStream to MySQL
	 */
	public int bulkLoadFromInputStream(String loadDataSql,
			InputStream dataStream) throws SQLException {
		if(dataStream==null){
			logger.info("InputStream is null ,No data is imported");
			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;
	}

	public static void main(String[] args) {
		String testSql = "LOAD DATA LOCAL INFILE 'sql.csv' IGNORE INTO TABLE test.test (a,b,c,d,e,f)";
		InputStream dataStream = getTestDataInputStream();
		BulkLoadData2MySQL dao = new BulkLoadData2MySQL();
		try {
			long beginTime=System.currentTimeMillis();
			int rows=dao.bulkLoadFromInputStream(testSql, dataStream);
			long endTime=System.currentTimeMillis();
			logger.info("importing "+rows+" rows data into mysql and cost "+(endTime-beginTime)+" ms!");
		} catch (SQLException e) {
			e.printStackTrace();
		}
		System.exit(1);
	}

}

 

 

 

 

 



提示:
 String testSql ="LOAD DATA LOCAL INFILE 'sql.csv' IGNORE INTO TABLE test.test (a,b,c,d,e,f)";




        使用setLocalInfileInputStream方法,会直接忽略掉文件名称,而直接将IO流导入到数据库中。
 


参考:
http://assets.en.oreilly.com/1/event/21/Connector_J%20Performance%20Gems%20Presentation.pdf
http://jeffrick.com/2010/03/23/bulk-insert-into-a-mysql-database/

 

 

 

原创文章,欢迎转载,转载请注明出处:http://blog.youkuaiyun.com/seven_3306/article/details/9237495

欢迎关注我的公众号(无广告、不卖课,只分享技术干货)

在这里插入图片描述

<think>我们正在使用Delphi10通过LOADDATAINFILE命令导入CSV文件MySQL数据库。LOADDATAINFILEMySQL提供的一个高效导入数据的方式,它可以直接将文件内容加载到数据库表中。步骤:1.确保MySQL服务器允许使用LOADDATAINFILE命令(需要FILE权限)。2.确保CSV文件格式正确,并且与目标表的结构匹配。3.在Delphi中,我们可以使用TADOQuery或TFDQuery(FireDAC组件)来执行LOADDATAINFILE语句。注意事项:-文件路径:LOADDATAINFILE需要文件MySQL服务器上,或者使用LOCAL关键字(如果客户端和服务器在同一台机器上,或者客户端有权限读取文件并传输给服务器)。-字符集:确保文件字符集与数据库表字符集匹配,否则可能出现乱码。-字段分隔符:CSV文件通常使用逗号分隔,但也要注意是否有引号包围字段。示例代码(使用FireDAC组件):```delphivarQuery:TFDQuery;beginQuery:=TFDQuery.Create(nil);tryQuery.Connection:=YourConnectionComponent;//你的数据库连接组件,如TFDConnectionQuery.SQL.Text:='LOADDATALOCALINFILE'''+'C:\path\to\your\file.csv'''+'INTOTABLEyour_table'+'FIELDSTERMINATEDBY'','''+'ENCLOSEDBY''"'''+'LINESTERMINATEDBY''\r\n'''+//根据实际换行符调整,可能是'\n''IGNORE1LINES';//如果CSV有标题行,则忽略第一行Query.ExecSQL;finallyQuery.Free;end;end;```重要提示:-使用`LOCAL`关键字时,文件在客户端机器上。如果使用`LOCAL`,则文件必须在MySQL服务器上,并且MySQL进程有读取权限。-使用`LOCAL`需要MySQL客户端库支持,并且在连接字符串中设置`LOCAL_INFILE=1`。对于FireDAC,可以在TFDConnection的Params中添加`"LOCAL_INFILE=1"`。例如,在TFDConnection的Params属性中加入:```User_Name=your_usernamePassword=your_passwordDatabase=your_databaseServer=localhostDriverID=MySQLLOCAL_INFILE=1```另外,确保MySQL服务器的配置允许本地加载。在MySQL的配置文件(如my.ini)中,设置:```[mysqld]local_infile=1```安全考虑:使用LOADDATALOCALINFILE可能存在安全风险,因为它可以读取客户端文件系统上的任何文件(如果SQL语句被注入攻击)。因此,确保你的应用程序有足够的安全措施。如果遇到权限问题,请检查MySQL用户是否有FILE权限(对于使用LOCAL的情况)或LOADDATALOCAL的权限(对于使用LOCAL的情况)。相关问题:1.如何处理导入过程中出现的错误(如重复键、数据类型匹配)?2.如何提高大文件导入的性能?3.在导入过程中如何显示进度?</think>### Delphi 10 通过 LOAD DATA INFILE 导入 CSV 到 MySQL 在 Delphi 10 中使用 `LOAD DATA INFILE` 导入 CSV 数据MySQL 是高效的方法,尤其适合大批量数据导入。以下是完整实现步骤: #### 一、核心代码实现 ```delphi uses FireDAC.Phys.MySQL; // FireDAC 组件库 procedure ImportCSVToMySQL; var FDConnection: TFDConnection; FDQuery: TFDQuery; begin // 创建数据库连接 FDConnection := TFDConnection.Create(nil); FDQuery := TFDQuery.Create(nil); try // 配置 MySQL 连接参数 FDConnection.Params.Add('DriverID=MySQL'); FDConnection.Params.Add('Server=127.0.0.1'); FDConnection.Params.Add('Database=your_database'); FDConnection.Params.Add('User_Name=root'); FDConnection.Params.Add('Password=123456'); FDConnection.Params.Add('CharacterSet=utf8'); // 字符集匹配 CSV FDConnection.Params.Add('LOCAL_INFILE=1'); // 关键:启用本地文件加载 FDConnection.Connected := True; FDQuery.Connection := FDConnection; // 构建 LOAD DATA INFILE 命令 FDQuery.SQL.Text := 'LOAD DATA LOCAL INFILE ''C:\data\yourfile.csv'' ' + 'INTO TABLE your_table ' + 'FIELDS TERMINATED BY '','' ' + 'ENCLOSED BY ''"'' ' + 'LINES TERMINATED BY ''\r\n'' ' + 'IGNORE 1 ROWS'; // 忽略 CSV 标题行 // 执行导入 FDQuery.ExecSQL; ShowMessage('数据导入成功!'); finally FDQuery.Free; FDConnection.Free; end; end; ``` #### 二、关键配置说明 1. **MySQL 权限配置** ```sql GRANT FILE ON *.* TO 'your_user'@'localhost'; ``` 在 `my.ini` 配置文件中启用: ```ini [mysqld] local_infile=1 ``` 2. **CSV 文件要求** - 文件路径:必须是 MySQL 服务可访问的路径(建议用完整路径) - 编码格式:与数据库一致(如 UTF-8) - 列顺序:与目标表字段顺序完全匹配 3. **FireDAC 特殊参数** ```delphi FDConnection.Params.Add('LOCAL_INFILE=1'); // 必须添加 ``` #### 三、错误处理方案 ```delphi try FDQuery.ExecSQL; except on E: EFDDBEngineException do ShowMessage('导入失败: ' + E.Message); end; ``` 常见错误处理: - **Error 2068 (HY000)**:检查 `LOCAL_INFILE=1` 是否配置 - **Error 1366 (HY000)**:字符集匹配(确保 CSV 和数据库均为 UTF-8) - **Error 1261 (HY000)**:列数据与目标类型匹配 #### 四、性能优化建议 1. 大文件导入时临时禁用索引: ```sql ALTER TABLE your_table DISABLE KEYS; -- 执行导入... ALTER TABLE your_table ENABLE KEYS; ``` 2. 使用事务提升速度: ```delphi FDConnection.StartTransaction; try FDQuery.ExecSQL; FDConnection.Commit; except FDConnection.Rollback; end; ``` > **安全提示**:生产环境中避免硬编码密码,建议使用加密配置文件或环境变量存储凭证[^5]。 ###
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值