【问题标题】:mysql statement->fetch() failing while loopingmysql 语句-> fetch() 在循环时失败
【发布时间】:2018-03-30 10:56:07
【问题描述】:

我有一个使用 PHP (5.6.28) 和 Mysql 编写的 REST API。我想要实现的目标是从数据库中提取 135k 条记录。

查询执行得很好,我可以很好地得到行数。但是在循环遍历结果并将它们添加到数组时,php 会停止执行而没有错误。

我检查了 php 错误(顺便说一句没有记录错误),增加了最大执行时间(300 秒),内存(256M)量等。当从 mysql 工作台执行时,sql 查询有效(在 15 秒内完成)。 PHP 脚本会在 16 秒内死掉。

我们如何调试这样的东西?或者你的解决方案是什么?您可以在下面找到代码。

set_time_limit(300); //allow php to run until query completes
ini_set('max_execution_time', 300);

$sql = "SELECT client_delivery_item_dest_address1, client_delivery_item_dest_address3,  client_delivery_item_volume,
        client_delivery_item_transport_charge, client_delivery_item_waybill_number,client_deliveries.client_delivery_insert_date,
        client_delivery_item_barcode 
        FROM client_deliveries 
        INNER JOIN client_delivery_items ON client_deliveries.client_delivery_id = client_delivery_items.client_delivery_id              
        WHERE " . $date_where . " " . $branch_where . " 
        AND client_deliveries.source_branch_id=3 AND client_deliveries.is_transfer=0 
        ORDER BY client_delivery_insert_date ASC";

$statement = $this->prepare($sql);
$statement->bind_result($client_delivery_item_dest_address1, $client_delivery_item_dest_address3, $client_delivery_item_volume,
                         $client_delivery_item_transport_charge, $client_delivery_item_waybill_number, $client_delivery_insert_date, $client_delivery_item_barcode);

//return array('sql' => $sql); used this to get the actual that is running

if (!$statement->execute()) {
    return array('error' =>  "Execute failed: (" . $stmt->errno . ") " . $stmt->error); //execution doesnt fail apparently
}  

$statement->store_result();
//return array('rowcount' => $statement->num_rows); returns 134411 it is the correct number of rows

while ($statement->fetch()) {
    $delivery_item                   = array();            
    $delivery_item['store_code']     = $client_delivery_item_dest_address1;
    $delivery_item['store_city']     = $client_delivery_item_dest_address3;
    $delivery_item['volume']         = $client_delivery_item_volume;
    $delivery_item['price']          = $client_delivery_item_transport_charge;
    $delivery_item['waybill_number'] = $client_delivery_item_waybill_number;
    $delivery_item['delivery_date']  = $client_delivery_insert_date;
    $delivery_item['barcode']        = $client_delivery_item_barcode;
    array_push($delivery_items, $delivery_item);

    //return $delivery_items; returns first row correctly
} 

//the execution dies before here

$statement->close();
return $delivery_items;

【问题讨论】:

  • 在处理一定数量的行后使用迭代器检查内存使用量,结果脚本使用了 285mb 的内存,因此脚本失败。我增加了内存限制,它解决了这个问题。关于如何减少内存消耗的任何想法?

标签: php mysql mysqli pdo


【解决方案1】:

如您所述,您超出了内存限制,在 PHP 5.6 中默认为 128MB。

您正在获取 135k 行。请注意,store_result() 在开始获取之前会将整个 结果集传输到 PHP 的内存中。在这种模式下,fetch() 实际上只是对已经在内存中的结果集进行迭代。因此,当您填充 $delivery_items 数组时,实际上是将结果集存储在内存中两次。

另一种方法是调用use_result(),它不会预取结果集。每次调用 fetch() 都会从 MySQL 服务器获取下一行。这是处理大型结果集时减少内存使用的常用方法。

然后您将 135k 行存储在一个数组中。每行有七个字段。我猜他们中的一些是数字的。您可以尝试将这些转换为二进制表示,因为本机整数比数字的字符串表示占用的空间更少。

$delivery_item['volume']         = (int) $client_delivery_item_volume;

我不知道您的数据,但这可能是您可以强制转换为 int 的唯一情况。 PHP 也有 float 类型,但我不建议将 float 用于货币值,因为四舍五入的问题。

我唯一的其他建议是避免将整个数组存储在内存中,而是构建您的应用程序,以便您可以一次处理每一行。为此,我将此函数编写为实现Iterator 的类,以封装查询并在获取行时一次返回一行。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-06-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-08
    • 2011-04-29
    • 2023-03-23
    相关资源
    最近更新 更多