gpt4 book ai didi

php - 如何查询,然后在较短的时间内处理大量数据

转载 作者:可可西里 更新时间:2023-11-01 08:16:31 26 4
gpt4 key购买 nike

我有一个很大的订单表。在设定的时间,我需要向他们中的大部分人发送一条 SMS 消息(不幸的是每个人的内容不同)(基于他们是否选择了该消息以及他们是否包含电话号码)。它可能在相当短的时间内达到 200,000 多个数字。 (现在并没有那么高,但理论上可以并且想要这样构建)。

他们不一定都必须立即发送,但是 - 在设定时间的 1-3 小时内发送是最理想的。

我正在使用带有 API 的服务来发送它们,所以这不是问题 - 问题是:

1) 量大如何处理

2) 如何知道他们都得到处理或重新处理那些没有

我不认为做一个 MySQL 查询来获取所有 200,000 多个电话号码,然后循环遍历是个好主意 - 我不得不假设这会吸收一个大量内存(?)。

所以 - 我想尝试一个 cron 作业,让它每分钟(大约)运行一次。在该脚本中,我可以提取大约 5,000 条记录,标记为“处理中”,并重复处理每条记录,然后将其行更新为“已发送”。

但这有很多潜在问题...如果 SMS 服务变慢而我无法将它们全部发布怎么办。如果出现不可预见的错误并且脚本在中途停止......等等。如果有任何事情导致该脚本无法发送所有 5000 条记录,我怎么知道要返回并重新处理哪些记录?

不仅是这个过程,我们还有其他一些过程 - 总的问题是如何处理我们需要以某种方式处理的大量行并知道每个行都已完成.

我希望我只是把这个废话复杂化了,并且有一些更简单的方法来处理。

如果我不清楚,请发表评论,我很乐意就任何方面进行进一步解释。

最佳答案

简短版:

  1. 不用担心内存消耗。只是不要尝试一次获取整个结果集。

  2. 您的想法是使用一个单独的表来列出每条短信,然后在知道它是否成功时更新该行通常是正确的方法(无论您是否在 cron 中执行都无关紧要).

  3. 如果您担心您的 SMS 提供商可能会丢弃您的某些请求,那么您可以使用 a ActiveMQ 实现您自己的排队机制。或类似的东西。然而,这种做法在很大程度上违背了使用提供者的目的。他们应该使用自己的队列,因此您无需担心。

详情:

SMS 服务应该会通知您成功或失败。大多数大容量 SMS 服务将您的消息排队,并一次将它们以 n 条消息为一组发送出去。然后,他们将通过某种回调或网络 Hook 通知您哪些消息已成功发送,哪些消息已失败。他们中的大多数还提供 API,让您检查是否已发送特定消息。您需要利用这些功能。

我认为您的 cron 方法走在正确的轨道上。

一种选择是从不“拉取”记录。相反,在现有表上有一列,指定它是否正在等待消息发送。这样,您无需执行 SELECT 和处理数十万行,而是执行一个简单的 UPDATE,然后当每个回调来自 API 时,您可以根据成功/失败重新更新行。

如果您遇到的情况是您可能一次为每行数据发送多条消息,那么显然这将行不通。对于要跟踪的每条消息,您必须有一个单独的表格,其中一行。

就您的内存问题而言,我认为这不是问题。只是不要获取整个结果集。相反,单独获取每一行。这将防止 mysql 返回整个数据集,因此您不需要将其保存在内存中。

来自 php.net

As mysqli_fetch_all() returns all the rows as an array in a single step, it may consume more memory than some similar functions such as mysqli_fetch_array(), which only returns one row at a time from the result set. Further, if you need to iterate over the result set, you will need a looping construct that will further impact performance. For these reasons mysqli_fetch_all() should only be used in those situations where the fetched result set will be sent to another layer for processing.

编辑/修改

解决评论/问题:

I can't pull just one entry per chron - that would take forever... I understand I shouldn't fetch the entire result set at once too, that's what led me to ask "then how else can I do it?

在 PHP 中(将 mysqli 与 mysqlnd 结合使用),当您进行查询时,它实际上并不返回数据。它会根据您的查询准备要返回的数据,但不会返回它。

当您使用 fetch_all 时,您要求的是整个结果。当您使用 fetch_array 时,您是在请求下一个 结果,并且您是在告诉 mysql 移动结果游标,以便您可以获得下一个结果。只要您不将每个结果都存储在内存中(在单独的变量中),就不存在内存问题。只需根据需要使用该行,然后获取下一个。是否是 cron 作业并不重要。
您不需要为每一行一次又一次地调用脚本。该脚本在一次调用中处理每一行。它只是一次读取一行,以节省内存。

这是一个脚本示例:

$mysqli = new mysqli("host", "user", "pass", "db");
$query = "SELECT * from TextMessages";
$result = $mysqli->query($query);
while ($row = $mysqli->fetch_array($result))
{
//this is the only thing you store in memory, one single row at a time
$row = $result->fetch_array(MYSQLI_ASSOC);

//go send the text message and do whatever else you need to do
if ($row["SomeSmsToken"] == null && $row["TextHasAlreadyBeenSentOrDateSentOrWhatever"] == false)
{
//$someSmsToken = $myTwilioObject->SendByRow($row);
//$this->UpdateRowToTellItThatItHasBeenSentToProviderAndIsWaitingForResponse($row,$someSmsToken);
//..etc...
//then go to the next row.
}
}
$result->free();

然后在一些回调脚本中你会做这样的事情。

$mysqli = new mysqli("host", "user", "pass", "db");
$query = "SELECT * from TextMessages where SomeSmsToken = '".$_POST["SomeTokenSentFromProviderInCallback"]."'";
$result = $mysqli->query($query);
while ($row = $mysqli->fetch_array($result))
{
$someObject->UpdateRowToSayThatTheTextWasSentOrItFailed($row,$_POST["SomeStatusSentFromProviderInCallback"]);
}

$result->free();

您还可以使用 mysqli_free_result完成后,释放 php 的 mysql 驱动程序消耗的所有内存。

来自 php.net:

You should always free your result with mysqli_free_result(), when your result object is not needed anymore.

编辑:如果你想要某种聪明的方法来处理“如果脚本超时怎么办”,我建议每分钟运行一次 cron。当它运行时,它应该检查它是否已经在运行,如果它还没有运行,那么你就运行它。该脚本将执行它的工作,直到它超时。
然后在一分钟内,cron 将再次启动它,并且由于它没有运行,它将再次运行并从中断的地方继续。

关于php - 如何查询,然后在较短的时间内处理大量数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25467764/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com