【问题标题】:Inserting 6000 records adding 5-10 seconds to the execution time [duplicate]插入 6000 条记录,执行时间增加 5-10 秒 [重复]
【发布时间】:2015-02-18 20:59:09
【问题描述】:

我的应用程序是数字标牌播放器。我们需要定期将远程播放证明数据发送回云端。缓存播放证明数据以帮助解决网络中断问题。

玩家一天可以轻松创建 6,000 条 POP 记录。当网络恢复正常时,这可能会导致从一个演示文稿转移到另一个演示文稿时出现相当严重的延迟。

我正在考虑使用 MySql 的异步模式来尝试缓解这种延迟......但我认为它并没有像我想的那样做。

我正在循环一个大型结果集,我仍然需要等待该循环完成。我是否捆绑了 6,000 个插页并一次全部发送?如果我仍在等待异步数据库调用,我的 AJAX 会在什么时候返回结果?它只是不能以这种方式工作吗? Google 返回的结果不到一页,而 Docs 并没有真正说明任何问题。

$sql = "SELECT MAX(time) from `pop` where `displayId` = ".$_GET['id'];
$res = $local->query($sql);
$row = $res->fetch_row();
//$log->lwrite("Last Updated is ".$row[0]);

$sql = "SELECT * FROM `pop` 
   WHERE `displayId` = ".$_GET['id']
   ." AND `time` > '".$row[0]."' LIMIT 1000";
$local->query($sql, MYSQLI_ASYNC);
if ($res = $local->reap_async_query()) {
    while ($row = $res->fetch_row()) {
        $log->lwrite(print_r($row, 1));
        $sql = "INSERT INTO `pop` 
          (`displayId`, `time`, `duration`, `presId`) 
          VALUES ('".$row[1]."', '".$row[2]."', '".$row[3]."', '".$row[4]."')";
        $remote->query($sql, MYSQLI_ASYNC);
    }
}

我已经接受了我将不得不以不同的方式来做这件事的想法。现在我只是好奇。 :)

我错过了什么吗?

感谢您的时间和帮助。

【问题讨论】:

  • 按原样与您的代码进行异步是没有意义的。您触发查询,然后开始循环等待它返回。您不妨启动一个同步查询并等待该调用返回。
  • 谢谢马克。我希望通过限制接口的数量来保持客户端的简单,但是通过将 POP 移动到它自己的 API/定时循环很容易解决。除非我遗漏了一些东西,否则无论格式如何,API 调用都不会返回,直到 ASYNC 调用已经获得。我只是想知道 ASYNC 如何真正为服务器端语言做任何有用的事情。
  • 一个可悲的 XY 问题教科书示例
  • YCS 指出的 sql 注入漏洞被否决

标签: php mysqli


【解决方案1】:

您的代码的第一个问题是您不应该在循环中执行 INSERT 语句。正确的做法是在单个 INSERT 语句中将多条记录插入数据库。这仅受 MySQL 的 max_allowed_pa​​cket 设置限制,该设置限制了查询的长度。通常这是相当大的,可以轻松容纳 6000 条记录。

$sql = "SELECT * FROM `pop` 
   WHERE `displayId` = ".(int)$_GET['id']
   ." AND `time` > '".$local->real_escape_string($row[0])."' LIMIT 1000";
$res = $local->query($sql) or die($local->error);// no async
$sql = '';
while ($row = $res->fetch_row()) {
    $row = array_map(array($local, 'real_escape_string'), $row);// must have sanitization
    if ($sql) $sql.= ',';// separate each record with a comma
    $sql.= "('".$row[1]."', '".$row[2]."', '".$row[3]."', '".$row[4]."')"
    $log->lwrite(print_r($row, 1));
}

$sql = "INSERT INTO `pop` 
  (`displayId`, `time`, `duration`, `presId`) 
  VALUES $sql";
$remote->query($sql, MYSQLI_ASYNC);// Do only one INSERT query instead of thousands

如果您在此处使用 MYSQLI_ASYNC,PHP 将不会等待插入完成,并且您的 AJAX 调用将在不等待远程数据库查询完成的情况下完成。

【讨论】:

  • 如果查询失败怎么办?
  • 另外,你的代码充满了 SQL 注入,这对于 2020 年写的答案来说是难以接受的
  • 注射是不是我的 OP,现在已修复。处理失败的查询不在问题范围内。
【解决方案2】:

您是在谈论您的 php 服务器和 DBMS 之间的网络吗?那样的话你还真的不了解 async mysqli。

使用异步查询的目的是应用 DML 而不会阻塞您的 PHP 代码。它不打算将命令排队到可能定期脱机的系统。您也不应该直接在 Internet 上公开 DBMS。它提供了一个窗口,您可以在其中使用 php 代码执行其他操作(例如运行其他查询或向客户端发送数据)。不打算在脚本完成后对数据进行操作。

有很多方法可以解决这个问题,其中大多数涉及在连接的两端运行守护程序 - 这可能是大多数人的主要限制。你还没有说你在每一端对系统有什么控制权。一个折衷的解决方案是使用卫星系统上的作业。当然,您不应该尝试在处理程序中为 Web 请求复制数据。

您是否尝试在本地复制整个表?如果是这样,为什么不使用 MySQL 的内置复制(通过 VPN 链接)。

【讨论】:

  • 数据库连接是一对多的,所以复制不起作用。我没有尝试通过 ASYNC 将命令排队...当我查询时间表数据时,requst 还会发回播放证明。在长时间的网络中断之后,我们希望快速恢复,但是 AJAX 调用因多次插入而变慢,即使以相对较小的批量上传也会增加 5-10 秒。我希望尽可能保持响应速度,而不必过多地限制批次。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-04-24
  • 1970-01-01
  • 2018-02-08
  • 2011-07-05
  • 2013-01-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多