我正在尝试批量编辑客户数据(几千条记录),但 Magento 不断耗尽可用内存来运行脚本。

经过一些测试后 memory_get_usage() 罪魁祸首似乎是 $customer->save() 方法似乎每次保存都会占用 5M 内存,但完成后不会释放它。

因此,当循环数千条记录时,它会耗尽内存。

这是我到目前为止所尝试过的:

$customer->clearInstance()

unset($customer)

但这似乎没有帮助。

下面是我的代码:

public function createCustomerAddress($customerAddressData, $email){
     $customer = Mage::getModel('customer/customer');

     $customer->setWebsiteId(Mage::app()->getWebsite()->getId());
     $customer->loadByEmail($email);

     $address   = Mage::getModel('customer/address');

     $address->addData($customerAddressData);
     $customer->addAddress($address);
     unset($address);
     try{
         $customer->save();
     }catch (Exception $e){
         var_dump($customerAddressData);
         var_dump($e->getMessage());
     }
    echo "\n" . "Before unsetting \n" . memory_get_usage() . "\n";
    $customer->clearInstance();
    unset($customer);
    echo "\n" . "After  \n" . memory_get_usage() . "\n";  // no difference than before
}

任何帮助将不胜感激。

附注我不确定是否 clearInstance() 函数(在 Mage_Core_Model_Abstract 中)可以做任何事情,如果有人对此有一些见解,如果它共享,我们将不胜感激:)

有帮助吗?

解决方案

我认为这与核心PHP问题有关,如果没有对核心Magento文件进行大量修改,该问题不容易解决。

完成此操作的唯一方法是使用解决方法:

我提议:

一个。如果从浏览器中调用了这一点,我将进行一个单独的Ajax呼叫,正如@Julien Lacal在他的答案中推荐的那样。

b。如果这一切都完成了服务器端,我将将其分解为两个脚本,其中1个。 createCustomerAddress 功能,使用邮政参数。然后,应使用第一个脚本中的卷曲来调用第二个脚本,以使第二个脚本的每个实例都孤立地运行,并且不受内存泄漏的影响。

其他提示

仅一旦没有引用该变量/对象,才会发布内存。我怀疑 - 一旦确认我的答案,Magento中的事件系统将模型的实例传递给了观察者,因此在内存中对其进行了引用。

但这是一个有据可查的问题。 (我在2011年发表的评论 http://www.magentocommerce.com/boards/viewthread/26561/).

我相信这是 Magento 所有平台和版本的一个已知问题。 unset() 或者 clearInstance() 不会有任何影响。 Mage::getModel() 在我遇到的所有实例(客户、产品、订单等)中都存在内存泄漏。我发现的唯一方法是在处理大量对象创建时,如果可能的话(可能不适合您)使用集合。当你的进程完成时,内存将被释放。

你的内存不足了吗?这通常不是问题,除非您是问题。如果内存不足,也许您可​​以尝试将批量进程分解为扩展中的单独运行。如果这是在 shell 脚本中,那么很容易将其分解。只需创建两个或多个文件,并使用预定义的客户范围逐个运行它们 entity_ids。

一个好的解决方法是为批量客户导入构建自己的管理模块,并让其使用ajax调用控制器以较小的集合导入数据。这样您就不会用完内存,因为每次您进行Ajax调用时,它都会启动一个新的过程,而不是不断分配内存,因为 Mage::getModel('customer/address')

似乎我在打电话时发现了重大的内存泄漏 Model::save()

将项目保存到数据库后,提到的方法中有以下代码:

$this->_getResource()->addCommitCallback(array($this, 'afterCommitCallback'))

这样,模型就会添加到静态参数 $_commitCallbacks. 。如果事务级别仅设置为零,则将删除该方法。

为了解决此问题,要么将以下代码添加到您自己的资源模型:

public function addCommitCallback($callback)
{
    return $this;
    //return parent::addCommitCallback($callback);
}

这仅适用于一种类型的资源模型,您需要对循环中经常更新的所有资源模型进行此操作。缺点是方法 afterCommitCallback() 您的模型从未被调用。

另一个(更好的)可能性是覆盖 commit()rollback() 方法并在每种情况下从变量中删除模型。

许可以下: CC-BY-SA归因
scroll top