MySQL使用Java从文件插入大型数据集

我需要从CSV文件中将大约180万行插入到MySQL数据库中。 (只有一张桌子)

目前使用Java来解析文件并插入每一行。

你可以想象这需要花费几个小时才能运行。 (10)

我之所以没有将它直接从文件传输到数据库中,是因为在将数据添加到数据库之前必须对其进行操作。

这个过程需要由那里的IT经理来运行。 所以我把它设置为一个很好的批处理文件,让它们在将新的csv文件放到正确的位置后运行。 所以,我需要通过将文件放到某个位置并运行批处理文件来很好地完成这项工作。 (Windows环境)

我的问题是,插入这么多数据的最快方法是什么? 大型插入,来自临时解析文件或一次插入一次? 还有其他一些想法吗?

第二个问题是,如何优化我的MySQL安装以允许非常快速的插入。 (还有一个点需要大量选择所有数据)

注意:该表最终将被删除,整个过程将在以后再次运行。

一些澄清:目前使用… opencsv.CSVReader解析文件,然后在每一行上插入。 我正在总结一些专栏而忽略其他专栏。

更多说明:本地DB MyISAM表

快速插入提示:

  • 使用LOAD DATA INFILE语法让MySQL解析并插入它,即使你必须修改它并在操作后提供它。
  • 使用此插入语法:

    插入表(col1,col2)值(val1,val2),(val3,val4),…

  • 在插入之前删除所有键/索引。

  • 在你所拥有的最快的机器中完成它(主要是IO,但RAM和CPU也很重要)。 数据库服务器,还有插入客户端,记住你将支付两倍的IO价格(一次读取,第二次插入)

我可能会选择一个很大的数字,比如10k行,并从CSV加载那么多行,按摩数据,然后进行批量更新,然后重复,直到你完成了整个csv。 根据数据的按摩/数量,1.8 mil的行不应该花费10个小时,更多的是1-2个小时,具体取决于您的硬件。

编辑:whoops,遗漏了一个相当重要的部分,你的con必须将autocommit设置为false,我复制它的代码是作为GetConnection()方法的一部分。

Connection con = GetConnection(); con.setAutoCommit(false); try{ PreparedStatement ps = con.prepareStatement("INSERT INTO table(col1, col2) VALUES(?, ?)"); try{ for(Data d : massagedData){ ps.setString(1, d.whatever()); ps.setString(2, d.whatever2()); ps.addBatch(); } ps.executeBatch(); }finally{ ps.close(); } }finally{ con.close(); } 

您是否绝对禁止在JDBC驱动程序中禁用自动提交?

这是JDBC客户端的典型性能杀手。

你应该真的在MySQL控制台上使用LOAD DATA来实现这一点而不是通过代码…

 LOAD DATA INFILE 'data.txt' INTO TABLE db2.my_table; 

如果你需要操作数据,我仍然建议在内存中操作,重写为平面文件,并使用LOAD DATA将其推送到数据库,我认为它应该更有效。

另一个想法是:您是否使用PreparedStatement通过JDBC插入数据?

根据您在插入数据之前需要对数据做些什么,您在速度方面的最佳选择是:

  • 在java中解析文件/使用数据执行所需操作/将“按摩”数据写入新的CSV文件/使用“load data infile”。
  • 如果您的数据操作是有条件的(例如,您需要检查记录是否存在并根据是插入还是更新等来执行不同的操作……)那么(1)可能是不可能的。 在这种情况下,您最好进行批量插入/更新。
    尝试找到适合您的最佳批量大小(从大约500-1000开始应该没问题)。 根据您用于表的存储引擎,您可能需要将其拆分为多个事务 – 具有单个跨度1.8M行不会对性能产生奇迹。
  • 您最大的性能问题很可能不是java而是mysql,特别是您插入的表上的任何索引,约束和外键。 在开始插入之前,请确保禁用它们。 最后重新启用它们将花费相当多的时间,但它比在每个语句之后让数据库评估它们更有效。

    由于事务的大小,您可能还会看到mysql性能问题。 您的事务日志将随着许多插入而变得非常大,因此在X次插入(例如10,000-100,000)之后执行提交也将有助于插入速度。

    从jdbc层,确保在PreparedStatement而不是普通的executeUpdate()上使用addBatch()和executeBatch()命令。

    您可以通过其Connector J JDBC驱动程序中的批处理function来提高MySQL / Java的批量INSERT性能。

    MySQL没有“正确”处理批处理(参见我的文章链接,底部),但它可以重写INSERT以利用奇怪的MySQL语法,例如,您可以告诉驱动程序重写两个INSERT:

     INSERT INTO (val1, val2) VALUES ('val1', 'val2'); INSERT INTO (val1, val2) VALUES ('val3', 'val4'); 

    作为单一声明:

     INSERT INTO (val1, val2) VALUES ('val1', 'val2'), ('val3','val4'); 

    (请注意,我并不是说需要以这种方式重写SQL; 驱动程序在可以的时候执行)

    我们这样做是为了我们自己的批量插入调查:它产生了一个数量级的差异。 与其他人提到的显式交易一起使用,你会看到总体上有很大改进。

    相关的驱动程序属性设置为:

     jdbc:mysql:///?rewriteBatchedStatements=true 

    请参阅: 使用MySQL Connector / J批量INSERT的性能提升10倍

    如果你使用LOAD DATA INFILE而不是插入每一行,会不会更快?

    我会跑三个线程……

    1)读取输入文件并将每一行推入转换队列2)从队列中弹出,转换数据,并推入db队列3)从db队列弹出并插入数据

    通过这种方式,您可以在db线程等待其IO完成时从磁盘读取数据,反之亦然

    如果您还没有,请尝试使用MyISAM表类型,请务必先阅读它的缺点。 它通常比其他类型的表更快。

    如果您的表具有索引,则删除它们通常会更快,然后在导入后将其添加回来。

    如果您的数据都是字符串,但更适合作为关系数据库,那么最好插入指示其他值的整数而不是存储长字符串。

    但总的来说,是的向数据库添加数据需要时间。

    这是一个有趣的读物: http : //dev.mysql.com/doc/refman/5.1/en/insert-speed.html