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

时间:2009-06-30 21:49:37

标签: java mysql

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

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

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

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

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

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

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

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

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

更多说明: 本地数据库 MyISAM表

12 个答案:

答案 0 :(得分:14)

快速插入的提示:

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

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

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

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

答案 1 :(得分:4)

我可能会选择一个很大的数字,比如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();
            }

答案 2 :(得分:2)

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

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

答案 3 :(得分:1)

你应该在MySQL控制台本身使用LOAD DATA,而不是通过代码......

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

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

答案 4 :(得分:1)

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

答案 5 :(得分:1)

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

  • 在java中解析文件/用数据做你需要的东西/将“按摩”数据写入新的CSV文件/使用“load data infile”。
  • 如果您的数据操作是有条件的(例如,您需要检查记录是否存在并根据是插入还是更新等执行不同的操作......)那么(1)可能是不可能的。在这种情况下,您最好进行批量插入/更新。实验找到适合您的最佳批量大小(从大约500-1000开始应该没问题)。根据您用于表的存储引擎,您可能需要将其拆分为多个事务 - 只需一个1.8M行就不会对性能产生奇迹。
  • 答案 6 :(得分:1)

    您最大的性能问题很可能不是java而是mysql,特别是您插入的表上的任何索引,约束和外键。在开始插入之前,请确保禁用它们。在最后重新启用它们将花费相当多的时间,但它比在每个语句之后让数据库评估它们更有效。

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

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

    答案 7 :(得分:1)

    通过使用Connector J JDBC驱动程序中的批处理功能,可以提高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:///<dbname>?rewriteBatchedStatements=true
    

    请参阅:A 10x Performance Increase for Batch INSERTs With MySQL Connector/J Is On The Way

    答案 8 :(得分:0)

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

    答案 9 :(得分:0)

    我会跑三个线程......

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

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

    答案 10 :(得分:0)

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

    如果你的表有索引,删除它们通常会更快,然后在导入后重新添加它们。

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

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

    答案 11 :(得分:0)