【发布时间】:2016-10-27 16:51:13
【问题描述】:
我在一个项目中工作,需要从远程服务器下载一个大文件,其中包含大约 500 万条记录的管道分隔数据。
下载完成后,需要将数据加载到数据库中。目前我一直在使用 MySQL 数据库,所以我没有考虑其他选项。
我使用面向对象的 MySQLi 并调用 LOAD DATA LOCAL INFILE 查询。
完成需要一个半小时到 2 小时,我需要一种显示进度的方法,我发现的唯一选项是 set_local_infile_handler 方法。似乎此方法的目的是允许程序员在将数据提供给查询之前更改数据的格式,但这是我发现的唯一选择,我想将其用于我的进度目的。
但我得到的只是:
PHP Fatal error: Uncaught Error: Call to undefined method mysqli::set_local_infile_handler() in C:\Repositories\project\tools\loaddata.php:65
我要求:
- 在我的代码中启用
mysqli::set_local_infile_handler的修复程序 - MySQLi 的替代方法可在加载数据时显示进度
- 使用 PHP 的其他替代方法
我尝试了一些想法(只有 10 万条记录):
- 从
new mysqli()更改为mysqli_init()和mysqli::real_connect()但唯一的原因是因为所有带有mysqli::set_local_infile_handler的示例都使用后一种形式。 - 我在
php.ini文件中取消了mysqli.allow_local_infile = On的注释,但是在此之前使用本地infile 查询运行代码没有问题,但我预计这可能会使麻烦的方法可见。我顺便停下来启动了服务器。 -
我调用了第二个查询
SHOW STATUS WHERE Variable_name in ('bytes_received','bytes_sent','innodb_buffer_pool_pages_data','innodb_buffer_pool_bytes_data','innodb_buffer_pool_pages_flushed','innodb_buffer_pool_read_requests','innodb_buffer_pool_reads','innodb_buffer_pool_write_requests','innodb_data_read','innodb_data_reads','innodb_data_writes','innodb_data_written','innodb_rows_inserted').我保留了所有这些变量,因为我没有仔细研究每个变量的确切含义,我注意到只有这些变量有意义地发生了变化,但似乎最后一个可能就足够了。但是我使用单独的文件执行此操作,我还不知道是否可以在同一代码中运行两个连接和两个线程以在后台运行最慢。事实上,当我找到
mysqli::set_local_infile_handler参考时,我正在浏览 php mysqli 回调函数。
我整天都在做这个,所以我一定尝试过其他一些东西,但要么我不记得它们,要么它们暂时无关紧要。 提前致谢。
我没有看到代码中的错误,它可以在没有本地 infile 方法的情况下工作,但这里是:
require_once("connectvars.php");
$filepath = $_SERVER["argv"][1];
$bloqIdx=0;
$conn = mysqli_init();
$conn->real_connect($mysvr,$myusr,$mypwd,$mydb);
// $conn = new mysqli($mysvr,$myusr,$mypwd,$mydb);
if ($conn->connect_error) {
trigger_error("SQL".$conn->connect_error,E_USER_ERROR);
die("Connect Error");
}
function countData($stream, &$buffer, $buflen, &$errmsg) {
global $bloqIdx;
$len = strlen($buffer);
if ($bloqIdx%1000==0) echo ".";
return $len;
}
function getRowsInserted() {
global $conn;
$result = $conn->query("show status where Variable_name='innodb_rows_inserted')";
$rowsInserted=0;
if ($result && $conn->affected_rows>0 && $row = $result->fetch_assoc()) $rowsInserted=$row["Value"];
$result->close();
return $rowsInserted;
}
$conn->query("truncate mytable");
$riStart = getRowsInserted();
$start = time();
$query = "LOAD DATA LOCAL INFILE '$filepath' INTO TABLE mytable FIELDS TERMINATED by '|' LINES TERMINATED BY '\n'";
$conn->set_local_infile_handler("countData");
$conn->query($query);
$conn->set_local_infile_default();
$minutes = abs(time() - $start)/60;
$riEnd = getRowsInserted()-$riStart;
echo "Finished Loading $riEnd rows from $filepath for ".round($minutes,2)." minutes\n";
$conn->close();
通过注释行:
$conn->set_local_infile_handler("countData");
和
$conn->set_local_infile_default();
代码有效,但没有进度信息。
http://php.net/manual/en/mysqli.set-local-infile-handler.php 的有效版本显然是 (PHP 5, PHP 7)
如果您确实需要测试代码,则作为参数的文本文件可以是:
1|one|alpha|C|2012-10-21 17:44:18
2|two|beta|C|2013-02-05 12:23:57
3|three|gamma|C|2012-12-10 07:18:09
4|four|delta|X|2012-11-27 11:51:32
5|five|phi|C|2013-01-07 14:03:29
和表脚本:
create table `mytable` (
`id` INT NOT NULL,
`num` CHAR(10) NULL,
`code` CHAR(13) NULL,
`status` CHAR(1) NULL,
`registered` DATETIME NULL,
INDEX `mycode` (`code` ASC, `registered` ASC),
PRIMARY KEY (`id`))
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8
COLLATE = utf8_bin;
【问题讨论】:
-
发布导致错误的代码。也许你说错了。
-
你可以派生一个子进程来完成下载和导入文件的工作,然后进入一个无限循环(每个都有一个 sleep() 和一些在错误或完成时退出的条件).. . 然后检查 innodb_rows_inserted (正如你提到的那样)。分叉:php.net/manual/en/function.pcntl-fork.php
-
好的,代码已添加。我更改了一些与私人数据相关的内容。
-
关于添加一个fork进程,如何在php中完成?我添加了我的代码,我希望我能得到一些代码作为答案。我开始检查你发布的链接,@AaronCicali。
-
如果 PHP 作为 Apache 模块安装,
pcntl_fork()不起作用,这就是我的情况。我正在检查pcntl_fork()的替代方案,但似乎唯一有用的是exec第二个文件代码。问题在于进度代码需要知道加载数据代码何时结束。现在我在命令行中运行代码,但稍后我需要将所有这些代码包含在网页中。
标签: php mysqli load-data-infile