2010-09-14 135 views
1

我有一个需要运行日常脚本的应用程序;每日脚本包含下载一个包含1,000,000行的CSV文件,并将这些行插入表中。优化海量MySQL插入

我在Dreamhost中托管我的应用程序。我创建了一个遍历所有CSV行的while循环,并为每个行执行INSERT查询。问题是我得到一个“500内部服务器错误”。即使我在1000个文件中以1000行记录,我也不能在同一个循环中插入40或50多行。

有什么方法可以优化输入吗?我也在考虑使用专用服务器;你怎么看?

谢谢!

佩德罗

+0

我不记得MySQL是否有批量插入操作,但这可能值得研究,而不是1000000个单独的插入语句。 – FrustratedWithFormsDesigner 2010-09-14 21:09:55

+0

你应该阅读以下内容:http://stackoverflow.com/questions/3523831/update-statement-running-for-too-long-or-not/3523903#3523903 – RobertPitt 2010-09-14 21:58:14

回答

13

大多数数据库具有优化的批量插入过程 - MySQL's is the LOAD DATA FILE syntax

要加载CSV文件,使用:

LOAD DATA INFILE 'data.txt' INTO TABLE tbl_name 
    FIELDS TERMINATED BY ',' ENCLOSED BY '"' 
    LINES TERMINATED BY '\r\n' 
    IGNORE 1 LINES; 
+0

+1,但我怀疑这将工作在共享主机场景,因为数据库服务器通常与Web服务器分离,因此无法访问用户文件。 – casablanca 2010-09-14 21:12:20

+0

@casablanca:PHP提供了一个包装界面IIRC,只是文件上传和权限的问题。 – 2010-09-14 21:22:08

+3

如果你不能上传数据文件到数据库服务器主机,那么使用'LOAD DATA LOCAL INFILE ...'然后你可以将数据文件上传到MySQL连接的客户端,在这种情况下,该连接是PHP应用程序主机。 – 2010-09-14 21:30:00

6

插入多个值,而不是做

insert into table values(1,2); 

insert into table values (1,2),(2,3),(4,5); 

最多的行以合适的数量时间。

或做批量导入,这是加载数据的最有效的方法,请参阅

http://dev.mysql.com/doc/refman/5.0/en/load-data.html

0

您可以创建的cronjob脚本,在一个请求增加了X个记录到数据库中。 Cronjob脚本将检查最后一次导入是否添加了所有需要的行,而他需要另外x行。

所以你可以添加尽可能多的你需要的行。

如果你有你的专用服务器,它会更容易。您只需运行所有插入查询的循环。

当然,您可以尝试将time_limit设置为0(如果它在dreamhost上工作)或将其设置为较大。

0

您的PHP脚本最有可能被终止,因为它超过了脚本时间限制。由于您在共享主机上,因此您运气不佳。

如果切换到专用服务器,并且如果获得shell访问权,最好的方法是使用mysql命令行工具插入数据。

0

OMG Ponies的建议非常棒,但我也'手动'将数据格式化为mysqldump使用的相同格式,然后以这种方式加载。非常快。

0

您是否尝试过进行交易?只需发送命令BEGIN到MySQL,做所有插入然后做COMMIT。这会加速显着,但像卡萨布兰卡说,你的脚本可能会超时。

3

通常我会说只是使用LOAD DATA INFILE,但似乎你不能与你的共享主机环境。

我没有使用MySQL的几年,但他们描述了如何加快插入批量插入一个很好的文件: http://dev.mysql.com/doc/refman/5.0/en/insert-speed.html

可从该中搜集到的一些想法:

  • 禁用/启用围绕插入的键:

    ALTER TABLE tbl_name DISABLE KEYS; ALTER TABLE tbl_name ENABLE KEYS;

  • 在插入语句中使用许多值。

    即:INSERT INTO表(COL1,COL2)VALUES(VAL1,VAL2),(..,..),...

    如果我没有记错,你可以为每个插入多达4096个值声明。

  • 甚至在启动之前运行FLUSH TABLES命令,以确保没有挂起的磁盘写入操作可能会影响插入性能。

我认为这会让事情变得更快。我会建议使用LOCK TABLES,但我认为禁用这些键使得这一点毫无意义。

UPDATE

我读这是通过禁用你的钥匙,你可能会删除一致性检查是为您的文件加载后的重要实现。你可以解决这个问题:

  • 确保您的表没有数据被载入“碰撞”与新的数据(如果你是从头开始,一个TRUNCATE语句将在这里很有用)。
  • 编写一个脚本来清理输入数据,以确保本地没有重复。无论如何,检查重复项可能会耗费大量数据库时间。
  • 如果你这样做,ENABLE KEYS不应该失败。
0

我以前自己遇到过这个问题,而且几乎没有什么问题,但是你需要做更多的工作来让它表现最好。

我发现,在我的情况下,我不能MySQL接受一个大的INSERT语句,但发现如果我将它分成几组10k INSERTS,就像没有建议​​那么它会这样做工作很快。有一点需要注意的是,当你这样做多个插入时,你很可能会遇到PHP的超时限制,但是这可以通过重置timout来避免,设置时间限制为set_time_limit($ seconds),我发现在每次成功插入后都这样做真的很好。

你必须小心这样做,因为你可能发现自己处于一个意外的循环中,无限制的时间,因此我建议通过检查MySQL报告的错误来测试以确保每个INSERT成功与mysql_errno()mysql_error()。您还可以通过检查受INSERT影响的行数与mysql_affected_rows()来捕获错误。您可以在第一次错误发生后停止。

0

如果你使用sqlloader,会更好。 您首先需要两件事控制文件,指定SQL Loader应该执行的操作以及要加载的第二个csv文件 以下链接可帮助您解决问题。 http://www.oracle-dba-online.com/sql_loader.htm

0

转到phpmyadmin并选择要插入的表格。

在“操作”选项卡下,然后在“表选项”选项/部分下,将存储引擎从InnoDB更改为MyISAM。

我曾经有过类似的挑战。 玩得开心。

+0

所以你说MyISAM比插入的InnoDB更好,你可能想阅读这个答案http://stackoverflow.com/a/1102425/2652018 – 2014-08-01 17:29:20

+0

我读过,但从我相信的经验来相信除此以外!我所做的大量插入['高达30,000']将它们全部包装在一个查询中,它的作用就像是一种魅力 – 2014-09-24 12:01:18

+0

,你需要达到一些基准配对,它可以用另一种方式。 “InnoDB是频繁写入的理想选择,MyISAM非常适合频繁读取(InnoDB在读取中也是如此)。” 这就是说使用和插入50M以上行的经验(geonames数据库) – 2014-09-24 14:50:48