优化大规模MySQL INSERT

时间:2010-09-14 21:05:09

标签: php sql mysql bulkinsert

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

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

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

谢谢!

佩德罗

10 个答案:

答案 0 :(得分: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;

答案 1 :(得分: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

答案 2 :(得分: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;

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

    I.e。:INSERT INTO表(col1,col2)VALUES(val1,val2),(..,..),...

    如果我没记错的话,每个插入语句最多可以有4096个值。

  • 在启动之前运行FLUSH TABLES命令,以确保没有可能影响插入性能的待处理磁盘写入。

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

<强>更新

我在阅读本文后意识到,通过禁用您的密钥,您可以删除对您的文件加载很重要的一致性检查。您可以通过以下方式解决此问题:

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

答案 3 :(得分:0)

您可以创建cronjob脚本,该脚本可以在一次请求时将x记录添加到数据库中。 Cronjob脚本将检查上次导入是否还没有添加所有需要的行,他需要另外x行。

因此,您可以添加所需数量的行。

如果您拥有专用服务器,则更容易。您只需使用所有插入查询运行循环。

当然你可以尝试将time_limit设置为0(如果它在dreamhost上工作)或者将它设置得更大。

答案 4 :(得分:0)

您的PHP脚本很可能因超出脚本时间限制而被终止。由于你是在一个共享主机上,你几乎没有运气。

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

答案 5 :(得分:0)

OMG小马的建议很棒,但我也“手动”将数据格式化为mysqldump使用的相同格式,然后以这种方式加载。很快。

答案 6 :(得分:0)

你尝试过做交易吗?只需将命令BEGIN发送给MySQL,然后执行所有插入操作COMMIT。这会显着加快速度,但像卡萨布兰卡所说的那样,你的剧本也可能会超时。

答案 7 :(得分:0)

我以前自己遇到过这个问题,而且几乎没有把它弄好,但是你需要多做一点才能让它发挥最佳效果。

我发现在我的情况下我不能接受一个大的INSERT语句,但发现如果我将它分成大约10k个INSERTS的组,就像nos建议那样它会做它的工作很快。需要注意的一点是,当执行这样的多个INSERT时,您很可能会达到PHP的超时限制,但是通过使用 set_time_limit($ seconds)重置时间可以避免这种情况,我发现这样做在每次成功的INSERT工作之后都很好。

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

答案 8 :(得分:0)

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

答案 9 :(得分:0)

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

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

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