【问题标题】:Need workaround against PHP 7.0.3 undefined function mysqli::set_local_infile_handler()需要针对 PHP 7.0.3 未定义函数 mysqli::set_local_infile_handler() 的解决方法
【发布时间】: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


【解决方案1】:

现在我将此案例报告为错误。今天添加了一条评论。此方法已被删除。

https://bugs.php.net/bug.php?id=77786

【讨论】:

    【解决方案2】:

    可以将它们分成 2 个不同的进程。

    第一个进程由 cron 作业每分钟运行一次,并寻找某种触发器(如 tmp 文件)的存在。当它找到触发器时,它会删除触发器(tmp 文件),下载大文件,将文件重命名为包含要插入的总行数的格式,然后开始将行插入到数据库中。

    第二个进程是 gui。它为用户提供了一个创建触发器(tmp 文件)的按钮,然后定期检查下载文件是否存在。一旦下载文件被第一个进程重命名为给定格式,它就可以解析文件名以获得总行数,然后定期查询数据库以确定现有行数并将其作为完成百分比呈现给用户。

    【讨论】:

    • 如果你想要比 cron 任务更灵敏的东西,你可以使用 inotify-tools (linux) 来监视文件系统事件。这样您就可以主动观察要创建的触发器文件,而不是等待下一次 cron 迭代。
    • 我想过类似的东西,除了触发器的想法,它看起来很脏但很实用。顺便说一句,不幸的是,我正在使用 Windows。我没有使用 WAMP,而是分别安装了 php、apache 和 mysql,我没有使用 IIS 的经验,也没有使用 sqlserver 的经验。所以现在任务计划程序是我用来运行批处理文件的 cron 等效程序,同时我使用第二个 cmd 终端来检查进度。我将尝试此解决方案并检查它是否适用于我的服务。
    • 解决方案,与我当时提到的要求相关,是使用 2 个不同的过程,所以这就是我将其标记为已接受答案的原因。在此要求已过时,大文件的提供者还允许通过 Web 服务进行通信,这成为当前的解决方案。
    猜你喜欢
    • 2013-01-05
    • 1970-01-01
    • 2018-11-05
    • 1970-01-01
    • 2017-07-27
    • 2011-05-04
    • 2012-08-15
    • 1970-01-01
    • 2020-04-13
    相关资源
    最近更新 更多