Magento : Fastest way to update a product attribute

The fastest way is to do direct selects and inserts/updates in the database, but that's not the safest. You can easily break stuff.

I use the Mage::getSingleton('catalog/product_action')->updateAttributes(...) approach.
It is fast, you can use it to bulk update product attributes, you can update an attribute value for a specific store.
I think it covers most of the cases needed.


Actually there are 3 ways to update an attribute on a product without save the full product. Depending on the code/requirements one can be faster that the other.

Method 1:

$product = Mage::getModel('catalog/product')->load($product_id);
$resource = $product->getResource();

$product->setData($attribue_code, $value);
$resource->saveAttribute($product, $attribute_code);

Method 2:

$updater = Mage::getSingleton('catalog/product_action');
$updater->updateAttributes(array($product_id), array( $attribute_code => $value), 0);

Method 3: (fastest)

 $update_resource = Mage::getResourceSingleton('catalog/product_action');
 $update_resource->updateAttributes(array($product_id), array( $attribute_code => $value), 0);

All above method are much faster that save the entire product anyway there are some major performance difference:

Method 1:

  • is the fastest, but required you to load the product.
  • it doesn't trigger the reindex event (that is why is faster)
  • it works in frontend

Method 2:

  • allows to bulk update product
    (you can pass multiple product and multiple attributes)
  • it triggers the mass action event and the relative dependent reindex
  • it does not work in frontend

Method 3:

  • it is like method 2, but does not call any other observer/indexer
    (so it is a mixed approach between method 1 and 2)

Method 3 is the more flexible one anyway you'll have to reindex those product/attributes manually. (in order to get them updated on frontend)
It may be useful if you want to update a lot of products fast and then call the reindex at the end.
(if you use method 2 a reindex for each product is called after the update and these multiple calls make the whole process slow)

To manually reindex a single product see the functions provided by Mage_Catalog_Model_Product_Flat_Indexer such as:

  • updateAttribute($attributeCode, $store = null, $productIds = null)
  • updateProduct($productIds, $store = null)
  • ...

Update

I'm looking for the fastest and reliable method for mass attribute update

"Mass attribute update" for attributes or products?

Think updating multiple attributes is alread answered, but for products this can be usefull ...

If you want to update products from collection, you should not do this ...

foreach ($collection as $product) {
    $product->setSomeData(...);
    # not here
    $product->save();
}

This will dispatch events, rebuild pricerules and indices. With this no events (and some other things) are skipped and is lot faster.

foreach ($collection as $product) {
    $product->setSomeData(...);
}
$collection->save();

To avoid pricerule updates, you can add ...

$product->setIsMassupdate(true);

To disable/enable reindex on the fly take a look at this ... https://github.com/Flagbit/Magento-ChangeAttributeSet/commit/676f3af77fec880bc64333403675d183e8639fae

/**
 * Set indexer modes to manual
 */
private function _storeRealtimeIndexer()
{
    $collection = Mage::getSingleton('index/indexer')->getProcessesCollection();
    foreach ($collection as $process) {
        if($process->getMode() != Mage_Index_Model_Process::MODE_MANUAL){
            $this->_index[] = $process->getIndexerCode();
            $process->setData('mode', Mage_Index_Model_Process::MODE_MANUAL)->save();
        }
    }

}
/**
 * Restore indexer modes to realtime an reindex product data
 */
private function _restoreRealtimeIndexer()
{
    $reindexCodes = array(
        'catalog_product_attribute',
        'catalog_product_flat'
    );
    $indexer = Mage::getSingleton('index/indexer');
    foreach ($this->_index as $code) {
        $process = $indexer->getProcessByCode($code);
        if (in_array($code, $reindexCodes)) {
            $process->reindexAll();
        }
        $process->setData('mode', Mage_Index_Model_Process::MODE_REAL_TIME)->save();
    }
}

And also flushing cache before mass (product) update can increase performance ...

Mage::app()->getCacheInstance()->flush();

Some numbers from debugging here: https://github.com/Flagbit/Magento-ChangeAttributeSet/issues/16


Mage::getSingleton('catalog/product_action')->updateAttributes(...) seems not to be the fastest method ... at least not with mutlistore setup and flat tables turned on ...

  • saveAttribute()

    $product = Mage::getModel('catalog/product')->load($productId);
    $resource = $product->getResource();
    $product->setData($attributeCode, $attributeValue);
    $resource->saveAttribute($product, $attributeCode);
    
    • Total Incl. Wall Time (microsec): 437,787 microsecs
    • Total Incl. CPU (microsecs): 423,600 microsecs
    • Total Incl. MemUse (bytes): 4,433,848 bytes
    • Total Incl. PeakMemUse (bytes): 4,395,128 bytes
    • Number of Function Calls: 25,711
  • updateAttributes()

    Mage::getSingleton('catalog/product_action')->updateAttributes(
        array($productId),
        array($attributeCode => $attributeValue),
        $storeId
    );
    
    • Total Incl. Wall Time (microsec): 3,676,950 microsecs
    • Total Incl. CPU (microsecs): 3,122,064 microsecs
    • Total Incl. MemUse (bytes): 8,174,792 bytes
    • Total Incl. PeakMemUse (bytes): 8,199,192 bytes
    • Number of Function Calls: 150,132
  • updateAttributes() (resource singleton)

    Mage::getResourceSingleton('catalog/product_action')->updateAttributes(
        array($productId),
        array( $attributeCode => $attributeValue),
        $storeId
    );
    
    • Total Incl. Wall Time (microsec): 94,155 microsecs
    • Total Incl. CPU (microsecs): 48,568 microsecs
    • Total Incl. MemUse (bytes): 1,426,304 bytes
    • Total Incl. PeakMemUse (bytes): 1,370,456 bytes
    • Number of Function Calls: 2,221