长话短说,如果我把PHP脚本分解成小块,我最终可以让我的所有代码运行。但是,我现在有一个脚本,它读取一个巨大的CSV文件并将每一行插入到MySQL数据库中。每次我想更新我的网站时,我都要经历拆分文件的繁重过程,而不是每次都要经历拆分文件的繁重过程,我只是想让这个脚本按照我知道它应该的方式工作。
我已经得到它插入大约10,000行之前在不同的web服务器上,但至少有7倍,在文件中,它在它完成之前崩溃。
所以,故事是,在一个服务器上,它在应该停止之前停止,而在另一个服务器上,它根本没有运行…它只会在30秒后发出500个错误。
Apache错误日志告诉我,当脚本死亡时,以下几行:
[Tue Aug 23 13:09:04 2011] [warn] [client 71.168.85.72] mod_fcgid: read data timeout in 40 seconds
[Tue Aug 23 13:09:04 2011] [error] [client 71.168.85.72] Premature end of script headers: newcsvupdater.php
我在脚本的顶部有这两行:
set_time_limit(0);
ini_set('memory_limit','256M');
,因为之前我有一个致命的内存分配错误,因为显然将一个大文件分割成数组是内存密集型的。
插入代码:
$file = "./bigdumbfile.csv"; // roughly 30mb
$handle = fopen($file, r);
$firstentry = 0;
while($csv = fgetcsv($handle))
{
if($firstentry == 0)
{
$firstentry++; // skips the top row of field names
}
else
{
// unimportant conditional code omitted
$checkforexisting = mysql_query("SELECT * FROM DB_TABLE WHERE ".
"id_one = '".$csv[0]."' AND id_two = '".$csv[2]."'");
$checknum = mysql_num_rows($checkforexisting);
if($checknum == 0)
{
if(!mysql_query("INSERT INTO DB_TABLE ".
"(id_one, data_one, id_two, data_two, ".
/* so on for 22 total fields */")
VALUES ('".addslashes($csv[0])."', '".
addslashes($csv[1])."', '".
addslashes($csv[2])."', '".
addslashes($csv[3])."' "/* ditto, as above */))
{
exit("<br>" . mysql_error());
}
else
{
print_r($csv);
echo " insert complete<br><br>";
}
}
}
}
echo "<br><b>DB_TABLE UPDATED";
我以前因为这个不得不把大的任务分开,我已经厌倦了。我敢肯定我做错了很多,因为我完全是自学的,通常写的都是意大利面条,所以请不要保留。
要增加脚本的时间限制,您需要为您的站点编辑虚拟主机配置:
http://www.moe.co.uk/2009/08/17/php-running-under-mod_fcgid-read-data-timeout-in-40-seconds-on-plesk/(mod_fcgid的超时覆盖PHP的超时)
要使您的脚本更快(因此您可能不需要执行上述步骤,这在共享主机上可能不可能),请尝试以下操作:
提前准备要插入的所有信息以进行批量插入。查询应该看起来像这样:
INSERT IGNORE INTO (id_one, data_one, id_two, data_two) VALUES
(1, 'apple', 3, 'banana'),
(4, 'pear', 5, 'orange)
IGNORE部分应该具有相同的效果,即提前检查记录是否已经存在(如果存在,则不会插入该记录,而是继续插入下一个记录)。
您可以使用SQL插入批量值,这应该会减少查询运行的时间(往返可能是瓶颈)。
INSERT INTO table (cola,colb...)
VALUES
(vala,valb...),
(valc,vald...)
大多数时候,当你做这样的大插入时,你想要异步地做它,这意味着你把文件转储到某个地方,离线处理它,然后通知用户它已经完成了,而不是让页面加载直到完成。
我还看到你在插入之前做了一个存在性检查。您可能需要考虑选择"可能"匹配的行,然后在PHP端执行检查(使用散列),而不是每次都运行该查询。
似乎你在Apache上超时了,而不是在PHP上。Set_time_limit函数用于php-script, apache不知道它
我的第一反应是不用PHP使用mysqlimport
或更好的LOAD DATA INFILE
来完成所有这些。
LOAD DATA INFILE ./bigdumbfile.csv INTO TABLE tbl_name;