使用大 CSV 更新数据库
Posted
技术标签:
【中文标题】使用大 CSV 更新数据库【英文标题】:Update database with big CSV 【发布时间】:2012-08-19 17:38:08 【问题描述】:我需要每天更新我的 mysql 中的 10.000 个项目。我已上传 CSV 文件并尝试更新我的数据库数据。我正在分叉两个领域。我的问题是它有效,但在某些时候我得到 504 Gateway Time-out 并且我的上传无法完成该过程。
这是我的代码
if(is_uploaded_file($_FILES["filename"]["tmp_name"]))
//
move_uploaded_file($_FILES["filename"]["tmp_name"], "".$_SERVER["DOCUMENT_ROOT"]."/upload/".$_FILES["filename"]["name"]);
$file_path="".$_SERVER["DOCUMENT_ROOT"]."/upload/".$_FILES["filename"]["name"]."";
$file=file_get_contents("".$file_path."");
$file=iconv("windows-1251", "utf-8",$file);
file_put_contents("".$file_path."",$file);
if(!setlocale(LC_ALL, 'ru_RU.utf8')) setlocale(LC_ALL, 'en_US.utf8'); if(setlocale(LC_ALL, 0) == 'C') die(' (ru_RU.utf8, en_US.utf8)');
if (($handle_f = fopen($file_path, "r")) !== FALSE)
// csv
while (($data_f = fgetcsv($handle_f,99999,";"))!== FALSE)
// ean13
$sql="SELECT id_product FROM ps_product WHERE reference = '".$data_f[0]."'";
$id_product = Db::getInstance()->getValue($sql,0);
// ,
if ($id_product)
$sql=mysql_query("UPDATE `ps_product` SET `quantity` ='".$data_f[1]."' WHERE `reference`='".$data_f[0]."'");
echo "<p style='color:green'>Items<b>".$data_f[0]."</b> updated</p>";
else
echo "<p style='color:red '>Items<b>".$data_f[0]."</b> not found</p>";
echo "<b>Update is complited</b>";
else
echo "Can`t open imported file";
else
echo '
<h2>Quantity update:</h2>
<form action="'.$_SERVER["php_SELF"].'" method="post" enctype="multipart/form-data">
<input type="file" name="filename"><br>
<input type="submit" value="Load"><br>
</form>
';
现在我在 excel 中拆分了许多文件,每次失败约 1000 个文件并更新数据库。这需要一些时间。你能给我一个想法,或者我的代码中可能有一些错误?使用 ajax 更新,我认为不会有帮助。
【问题讨论】:
【参考方案1】:504 Bad Gateway Timeout
是当您的网关/代理服务器没有从您的处理服务器获得timely response
时。在这种情况下,即使处理服务器确实完成了所有更新,因为它没有向您的代理发送任何消息以告知其仍在进行处理,代理也会关闭连接。
在这种情况下,使用 ajax 进行更新并不是一个好主意。但是,有一个名为 heartbeat pattern
的 ajax design pattern
可以帮助您。基本思路是这样的:
-
您正常调用处理页面以启动 CSV 更新
此外,您在页面中设置了一个 javascript 计时器,它每 X 分钟对服务器页面进行一次 ajax 调用
服务器以简单的消息进行响应。甚至可以只是一个字符
您的客户端 ajax 可以忽略此消息,或打印它或其他任何内容
X 分钟后会发生相同的过程。
与此同时,您在第一步中启动的 CSV 处理仍在继续
此continuous message request and response
称为heartbeat
。它有助于在您的服务器处理发生时保留connection alive
。一旦服务器处理结束并且您得到确认,您可以终止计时器并根据需要关闭连接。
当然,在处理服务器端,您可以执行其他操作,例如 increase timeout lenght
、add database indexes
等,以确保不会发生 php process timeout
。
但请注意,进程超时与网关超时不同。
【讨论】:
我也有一个想法,将数据拆分为 1000 并上传。我认为在 1000 行之后告诉 php 关闭查询并开始新的查询并非不可能 您可以考虑创建一种机制,使php执行1000行并向客户端发送响应。当客户端收到响应时,它再次调用执行下一个 1000 并再次响应的相同脚本。这样,用户只需点击一次按钮/开始请求,就可以无缝地批量处理。 Raidenace,所以如果我将在 json 中上传,updae 1000 编号并使用 json 最后插入编号接收并继续从这部分开始,json 递归生成,它会是单独的查询吗?不是很大,如果是,那么我认为它会有所帮助,如果在 php 中进行递归,它会起作用还是仍然是一个大查询? 是的,每次查询1000个。但是,您还需要确保处理回滚等问题,以防其中一次迭代失败。但是,是的,它不会很大,而是 1000 的倍数或您选择在每批中运行的任何其他数字【参考方案2】:您有参考索引吗?否则每次更新都需要很长时间。
另外,你有一些主要的 SQL 注入问题,并且 mysql 在贬值,切换到 mysqli。
【讨论】:
【参考方案3】:我需要将大型 CSV 加载到 MySQL 中,并且发现最好的解决方案是创建一个可以保存原始 CSV 数据的表。我使用与 CSV 相同的字段创建表,然后使用 LOAD DATA
进行批量导入,这很快,然后我可以使用 SQL 对数据执行必要的查询。
LOAD DATA LOCAL INFILE '$filename'
INTO TABLE $table_name
FIELDS TERMINATED BY '|'
LINES TERMINATED BY '\n'
一旦您在表中拥有 CSV 数据,您就可以简单地以块(LIMIT 和 OFFSET)伪代码运行查询,例如
DEFINE("BLOCK_SIZE",100);
// Get the total rows in CSV table so when can divide into blocks
SELECT SQL_CALC_FOUND_ROWS *
FROM my_csv_table
LIMIT 1;
$total_rows = SELECT FOUND_ROWS();
for($counter = 0; $counter < $total_rows; $counter += BLOCK_SIZE)
SELECT *
FROM my_csv_table
LIMIT BLOCK_SIZE
OFFSET $loop_counter;
foreach $row
// UPDATE YOUR REAL TABLE IF MATCHED
【讨论】:
以上是关于使用大 CSV 更新数据库的主要内容,如果未能解决你的问题,请参考以下文章