4

这条线以下的所有内容都已过时。Magento 只是很慢,仅此而已。


Magento 非常慢,如https://stackoverflow.com/questions/12580828/magento-saving-product-is-extremly-slow-but-profiler-shows-it-only-takes-1sec/12583078#12583078中所述

由于在 HostGator 上缺乏 root 权限而苦苦挣扎后,我最终分析了 Magento 调用自己。

这是其中一个结果:

Blue: timing 1982878436 Mage_Sales_Model_Mysql4_Quote begin<- 这是在输入Mage_Sales_Model_Mysql4_Quote'save方法时记录的。

Blue: timing 1982878436 Mage_Sales_Model_Mysql4_Quote 46<- 退出时会记录下来。

该数字1982878436是作为呼叫的 id 生成的随机数。number46是以秒为单位的时间。

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

正如我们所见19828784361712949075,2103820838被并行调用,每个都需要几十秒才能完成。我怀疑这三个调用之间存在一些锁定问题,使它们相互等待。有时当我保存产品时,Magento 甚至会报告操作失败,因为 MySQL 由于死锁而失败。

有人对此有任何想法吗?

4

2 回答 2

4

当运行 inloving 查询时INMySQL总是让最外层的表领先。

这意味着这个查询:

    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')})

将不得不扫描每条记录sales/quote并检查它,sales/quote_item而它又必须检查每个匹配的记录catalogrule/rule_product_price

如果有sales/quote比子查询返回的记录多得多的记录,这将很慢。

您可能希望将其重写为连接:

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

这样,优化器可以选择哪个表作为前导(当每个连接字段都被索引时,它应该是最小的表)。

于 2012-10-05T08:48:51.020 回答
2

它最终只是其他查询阻止了这些查询。在这个问题上有两个主要的放缓。

一个在 Mage/Sales/Model/Mysql4/Quote.php 中,有两个嵌套查询。我既不知道 MySQL 的缓存是如何工作的,也不知道如何在 HostGator 上配置 MySQL,所以我最终自己缓存了查询结果:

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)
        );
    }
}

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;
}

还有 Mage/CatalogRule/Model/Rule.php。在内部,这似乎是一个众所周知的重新索引问题。

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));
        }
    }
}

我认为让它响应全球环境应该是一个更好的方法。但是我没有时间,所以我复制了这个解决方案。

于 2012-09-29T01:48:33.550 回答