短版:
不用担心内存消耗。只是不要尝试一次获取整个结果集。
您的想法是使用单独的表格列出每条短信,然后在您知道它是否成功时更新该行通常是正确的方法(无论您是否在 cron 中执行此操作并不重要)。
如果您担心您的 SMS 提供商可能会丢弃您的一些请求,那么您可以使用 a ActiveMQ 或类似的东西来实现您自己的排队机制。但是,这在很大程度上违背了使用提供程序的目的。他们应该使用自己的队列,这样您就不必担心了。
详情:
短信服务应该会通知您成功或失败。
大多数高容量 SMS 服务将您的消息排队,并一次将它们以 n 条消息的块形式发送出去。然后,他们将通过某种回调或网络挂钩通知您,哪些消息已成功,哪些消息已失败。
它们中的大多数还提供 API,可让您检查是否已发送特定消息。您需要利用这些功能。
我认为你的 cron 方法走在正确的轨道上。
一种选择是永远不要“拉”记录。相反,在现有表上有一列指定它是否正在等待消息发送。这样,您无需执行 SELECT 并处理数十万行,而是执行简单的 UPDATE,然后当每个回调来自 API 时,您可以使用成功/失败重新更新行。
如果您可能会为每行数据一次发送多条消息,那么显然这不起作用。您必须有一个单独的表格,其中包含您要跟踪的每条消息的一行。
就您的记忆力而言,我认为这不是问题。只是不要获取整个结果集。相反,单独获取每一行。这将阻止 mysql 返回整个数据集,因此您不需要将其保存在内存中。
来自 php.net
由于 mysqli_fetch_all() 在单个步骤中将所有行作为数组返回,它可能比一些类似的函数(例如 mysqli_fetch_array() 一次只从结果集中返回一行)消耗更多的内存。此外,如果您需要对结果集进行迭代,您将需要一个循环结构来进一步影响性能。由于这些原因,mysqli_fetch_all() 应该只用于那些将获取的结果集发送到另一层进行处理的情况。
编辑/修正
解决评论/问题:
我不能在每个 chron 中只提取一个条目 - 这将花费很长时间......我
明白我也不应该一次获取整个结果集,那就是
是什么让我问“那我还能怎么做呢?
在 PHP(使用 mysqli 和 mysqlnd)中,当您进行查询时,它实际上并不返回数据。它会根据您的查询准备要返回的数据,但不会返回。
当您使用 fetch_all 时,您要求的是整个结果。
当你使用 fetch_array 时,你是在询问 next 结果,你是在告诉 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:
当您的结果对象不再需要时,您应该始终使用 mysqli_free_result() 释放结果。
编辑:
如果您想要某种聪明的方法来处理“如果脚本超时怎么办”,我建议每分钟运行一次 cron。
当它运行时,它应该检查它是否已经在运行,如果它还没有运行,那么你运行它。
该脚本会一直工作,直到超时。
然后在一分钟内,cron 将再次启动它,并且由于它没有运行,它会再次运行并从它停止的地方继续。