Reputation: 2691
Everything below this line is out of date. Magento is just slow, nothing less and nothing more.
Magento is extremely slow as said in https://stackoverflow.com/questions/12580828/magento-saving-product-is-extremly-slow-but-profiler-shows-it-only-takes-1sec/12583078#12583078
After some struggling due to lack of root privilege on HostGator, I end up profiling Magento calls myself.
Here's one of those results:
Blue: timing 1982878436 Mage_Sales_Model_Mysql4_Quote begin
<- this is logged when entering Mage_Sales_Model_Mysql4_Quote
's save
method.
Blue: timing 1982878436 Mage_Sales_Model_Mysql4_Quote 46
<- and this is logged when exiting.
The number 1982878436
is a random number generated as the id of the call. And number 46
is the time taken in seconds.
2012-09-26T06:36:16+00:00 DEBUG (7): Blue: timing 1982878436 Mage_Sales_Model_Mysql4_Quote begin
2012-09-26T06:36:18+00:00 DEBUG (7): Blue: timing 645597828 Mage_Log_Model_Mysql4_Visitor begin
2012-09-26T06:36:18+00:00 DEBUG (7): Blue: 645597828 Varien_Db_Adapter_Pdo_Mysql
2012-09-26T06:36:18+00:00 DEBUG (7): Blue: timing 645597828 Mage_Log_Model_Mysql4_Visitor 0
2012-09-26T06:36:18+00:00 DEBUG (7): Blue: timing 1712949075 Mage_Sales_Model_Mysql4_Quote begin
2012-09-26T06:36:24+00:00 DEBUG (7): Blue: timing 2103820838 Mage_Sales_Model_Mysql4_Quote begin
2012-09-26T06:36:56+00:00 DEBUG (7): Blue: timing 1999314779 Mage_Log_Model_Mysql4_Visitor begin
2012-09-26T06:36:56+00:00 DEBUG (7): Blue: 1999314779 Varien_Db_Adapter_Pdo_Mysql
2012-09-26T06:36:56+00:00 DEBUG (7): Blue: timing 1999314779 Mage_Log_Model_Mysql4_Visitor 0
2012-09-26T06:36:56+00:00 DEBUG (7): Blue: timing 504509596 Mage_Sales_Model_Mysql4_Quote begin
2012-09-26T06:36:56+00:00 DEBUG (7): Blue: timing 1887845167 Mage_Log_Model_Mysql4_Visitor begin
2012-09-26T06:36:56+00:00 DEBUG (7): Blue: timing 1887845167 Mage_Log_Model_Mysql4_Visitor 0
2012-09-26T06:37:02+00:00 DEBUG (7): Blue: timing 1887308594 Mage_GoogleOptimizer_Model_Mysql4_Code begin
2012-09-26T06:37:02+00:00 DEBUG (7): Blue: timing 1887308594 Mage_GoogleOptimizer_Model_Mysql4_Code 0
2012-09-26T06:37:02+00:00 DEBUG (7): Blue: 504509596 Varien_Db_Adapter_Pdo_Mysql
2012-09-26T06:37:02+00:00 DEBUG (7): Blue: timing 504509596 Mage_Sales_Model_Mysql4_Quote 6
2012-09-26T06:37:02+00:00 DEBUG (7): Blue: 1982878436 Varien_Db_Adapter_Pdo_Mysql
2012-09-26T06:37:02+00:00 DEBUG (7): Blue: timing 1982878436 Mage_Sales_Model_Mysql4_Quote 46
2012-09-26T06:37:02+00:00 DEBUG (7): Blue: 1712949075 Varien_Db_Adapter_Pdo_Mysql
2012-09-26T06:37:02+00:00 DEBUG (7): Blue: timing 1712949075 Mage_Sales_Model_Mysql4_Quote 44
2012-09-26T06:37:02+00:00 DEBUG (7): Blue: 2103820838 Varien_Db_Adapter_Pdo_Mysql
2012-09-26T06:37:02+00:00 DEBUG (7): Blue: timing 2103820838 Mage_Sales_Model_Mysql4_Quote 38
As we can see 1982878436
, 1712949075
, 2103820838
are called in parallel, and each took several tens of seconds to finish. I suspect that there is some lock issue among the three calls make them waiting for each other. Sometimes when I'm saving a product, Magento will even report action being failed because MySQL is failed due to deadlock.
Anyone has any ideas about this?
Upvotes: 4
Views: 2468
Reputation: 425411
When running queries inloving IN
, MySQL
always makes the outermost table leading.
This means that this query:
UPDATE {$this->getTable('sales/quote')} SET trigger_recollect = 1
WHERE entity_id IN (
SELECT DISTINCT quote_id
FROM {$this->getTable('sales/quote_item')}
WHERE product_id IN (SELECT DISTINCT product_id FROM {$this->getTable('catalogrule/rule_product_price')})
will have to scan each record in sales/quote
and check it against sales/quote_item
which in its turn will have to check each matched record against catalogrule/rule_product_price
.
If there are significantly more records in sales/quote
than the subquery returns, this will be slow.
You may want to rewrite it as a join:
UPDATE {$this->getTable('catalogrule/rule_product_price')} crpp
JOIN {$this->getTable('sales/quote_item')} sqi
ON sqi.product_id = crpp.product_id
JOIN {$this->getTable('sales/quote')} sq
ON sq.entity_id = sqi.quote_id
SET sq.trigger_recollect = 1
This way, optimizer may choose which table to make the leading (when every join field is indexed, it should be the smallest table).
Upvotes: 4
Reputation: 2691
It ends up just other queries blocked these queries. There two major slow down in this issues.
One is in Mage/Sales/Model/Mysql4/Quote.php, there are two nested queries. I know neither how MySQL's cache works nor how to configure MySQL on HostGator, so I end up caching the query result myself:
public function markQuotesRecollectOnCatalogRules()
{
/*
$this->_getWriteAdapter()->query("
UPDATE {$this->getTable('sales/quote')} SET trigger_recollect = 1
WHERE entity_id IN (
SELECT DISTINCT quote_id
FROM {$this->getTable('sales/quote_item')}
WHERE product_id IN (SELECT DISTINCT product_id FROM {$this->getTable('catalogrule/rule_product_price')})
)"
);
*/
$products = $this->_getReadAdapter()->fetchCol("SELECT DISTINCT product_id FROM {$this->getTable('catalogrule/rule_product_price')}");
$ids = $this->_getReadAdapter()->fetchCol("
SELECT DISTINCT quote_id
FROM {$this->getTable('sales/quote_item')}
WHERE product_id IN (?)", implode(',', $products)
);
if (count($ids) > 0)
{
$this->_getWriteAdapter()->query("
UPDATE {$this->getTable('sales/quote')} SET trigger_recollect = 1
WHERE entity_id IN (?)", implode(',', $ids)
);
}
}
And
public function markQuotesRecollect($productIds)
{
/*
$this->_getWriteAdapter()->query("
UPDATE `{$this->getTable('sales/quote')}` SET `trigger_recollect` = 1
WHERE `entity_id` IN (
SELECT DISTINCT `quote_id`
FROM `{$this->getTable('sales/quote_item')}`
WHERE `product_id` IN (?)
)", $productIds
);
*/
$ids = $this->_getReadAdapter()->fetchCol("
SELECT DISTINCT quote_id
FROM {$this->getTable('sales/quote_item')}
WHERE product_id IN (?)", $productIds
);
if (count($ids) > 0)
{
$this->_getWriteAdapter()->query("
UPDATE {$this->getTable('sales/quote')} SET trigger_recollect = 1
WHERE entity_id IN (?)", implode(',', $ids)
);
}
return $this;
}
And there is the Mage/CatalogRule/Model/Rule.php. Inside, it seems to be a well known reindexing problem.
public function applyAllRulesToProduct($product)
{
$this->_getResource()->applyAllRulesForDateRange(NULL, NULL, $product);
$this->_invalidateCache();
/*
$indexProcess = Mage::getSingleton('index/indexer')->getProcessByCode('catalog_product_price');
if ($indexProcess) {
$indexProcess->reindexAll();
}
*/
if ($product instanceof Mage_Catalog_Model_Product)
{
$id = $product->getId();
}
else
{
$id = $product;
}
if ($id)
{
$indexer = Mage::getResourceSingleton('catalog/product_indexer_price');
if ($indexer)
{
$indexer->reindexProductIds(array($id));
}
}
}
I think making it response to the global setting should be a better way. But I've got no time for it, so I copied this solution.
Upvotes: 2