我有一个需要运行日常脚本的应用程序;每日脚本包含下载一个包含1,000,000行的CSV文件,并将这些行插入表中。优化海量MySQL插入
我在Dreamhost中托管我的应用程序。我创建了一个遍历所有CSV行的while循环,并为每个行执行INSERT查询。问题是我得到一个“500内部服务器错误”。即使我在1000个文件中以1000行记录,我也不能在同一个循环中插入40或50多行。
有什么方法可以优化输入吗?我也在考虑使用专用服务器;你怎么看?
谢谢!
佩德罗
我有一个需要运行日常脚本的应用程序;每日脚本包含下载一个包含1,000,000行的CSV文件,并将这些行插入表中。优化海量MySQL插入
我在Dreamhost中托管我的应用程序。我创建了一个遍历所有CSV行的while循环,并为每个行执行INSERT查询。问题是我得到一个“500内部服务器错误”。即使我在1000个文件中以1000行记录,我也不能在同一个循环中插入40或50多行。
有什么方法可以优化输入吗?我也在考虑使用专用服务器;你怎么看?
谢谢!
佩德罗
大多数数据库具有优化的批量插入过程 - 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,但我怀疑这将工作在共享主机场景,因为数据库服务器通常与Web服务器分离,因此无法访问用户文件。 – casablanca 2010-09-14 21:12:20
@casablanca:PHP提供了一个包装界面IIRC,只是文件上传和权限的问题。 – 2010-09-14 21:22:08
如果你不能上传数据文件到数据库服务器主机,那么使用'LOAD DATA LOCAL INFILE ...'然后你可以将数据文件上传到MySQL连接的客户端,在这种情况下,该连接是PHP应用程序主机。 – 2010-09-14 21:30:00
插入多个值,而不是做
insert into table values(1,2);
做
insert into table values (1,2),(2,3),(4,5);
最多的行以合适的数量时间。
或做批量导入,这是加载数据的最有效的方法,请参阅
您可以创建的cronjob脚本,在一个请求增加了X个记录到数据库中。 Cronjob脚本将检查最后一次导入是否添加了所有需要的行,而他需要另外x行。
所以你可以添加尽可能多的你需要的行。
如果你有你的专用服务器,它会更容易。您只需运行所有插入查询的循环。
当然,您可以尝试将time_limit设置为0(如果它在dreamhost上工作)或将其设置为较大。
您的PHP脚本最有可能被终止,因为它超过了脚本时间限制。由于您在共享主机上,因此您运气不佳。
如果切换到专用服务器,并且如果获得shell访问权,最好的方法是使用mysql命令行工具插入数据。
OMG Ponies的建议非常棒,但我也'手动'将数据格式化为mysqldump使用的相同格式,然后以这种方式加载。非常快。
您是否尝试过进行交易?只需发送命令BEGIN
到MySQL,做所有插入然后做COMMIT
。这会加速显着,但像卡萨布兰卡说,你的脚本可能会超时。
通常我会说只是使用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
我读这是通过禁用你的钥匙,你可能会删除一致性检查是为您的文件加载后的重要实现。你可以解决这个问题:
我以前自己遇到过这个问题,而且几乎没有什么问题,但是你需要做更多的工作来让它表现最好。
我发现,在我的情况下,我不能MySQL接受一个大的INSERT语句,但发现如果我将它分成几组10k INSERTS,就像没有建议那么它会这样做工作很快。有一点需要注意的是,当你这样做多个插入时,你很可能会遇到PHP的超时限制,但是这可以通过重置timout来避免,设置时间限制为set_time_limit($ seconds),我发现在每次成功插入后都这样做真的很好。
你必须小心这样做,因为你可能发现自己处于一个意外的循环中,无限制的时间,因此我建议通过检查MySQL报告的错误来测试以确保每个INSERT成功与mysql_errno()或mysql_error()。您还可以通过检查受INSERT影响的行数与mysql_affected_rows()来捕获错误。您可以在第一次错误发生后停止。
如果你使用sqlloader,会更好。 您首先需要两件事控制文件,指定SQL Loader应该执行的操作以及要加载的第二个csv文件 以下链接可帮助您解决问题。 http://www.oracle-dba-online.com/sql_loader.htm
转到phpmyadmin并选择要插入的表格。
在“操作”选项卡下,然后在“表选项”选项/部分下,将存储引擎从InnoDB更改为MyISAM。
我曾经有过类似的挑战。 玩得开心。
所以你说MyISAM比插入的InnoDB更好,你可能想阅读这个答案http://stackoverflow.com/a/1102425/2652018 – 2014-08-01 17:29:20
我读过,但从我相信的经验来相信除此以外!我所做的大量插入['高达30,000']将它们全部包装在一个查询中,它的作用就像是一种魅力 – 2014-09-24 12:01:18
,你需要达到一些基准配对,它可以用另一种方式。 “InnoDB是频繁写入的理想选择,MyISAM非常适合频繁读取(InnoDB在读取中也是如此)。” 这就是说使用和插入50M以上行的经验(geonames数据库) – 2014-09-24 14:50:48
我不记得MySQL是否有批量插入操作,但这可能值得研究,而不是1000000个单独的插入语句。 – FrustratedWithFormsDesigner 2010-09-14 21:09:55
你应该阅读以下内容:http://stackoverflow.com/questions/3523831/update-statement-running-for-too-long-or-not/3523903#3523903 – RobertPitt 2010-09-14 21:58:14