[Magento] Cache 探索

虽然 Magento 非常慢,但是 Magento 开发团队对 Magento 的架构的优化却非常棒, 这篇文章记录了我研究和学习 Magento 缓存层的过程和心得。我一直想把 Magento 默认的缓存层改到 Memcached 或者 Redis 上试试,Magento 默认的缓存层是基于文件IO的,如果将缓存层改成基于NoSQL,性能应该可以再提高一点。

从 Magento 后台来看, Magento 默认的缓存类型有8种: Cache Types

首先来看的 Collections Data 这个缓存类型: 所有 collection 的基类是 Varien_Data_Collection_Db, 从类名就可以看出, 这个类提供了和数据库交互的功能。 可以猜测,collection 在读取数据时的逻辑是:

先检查是否使用缓存 如果不使用缓存,则直接查询数据库,并返回查询结果 如果使用缓存,则先查询缓存是否已经存在 如果缓存不存在,则查询数据库,并将查询结果写入缓存,然后返回数据 如果缓存存在,则直接读取缓存中的数据,并返回数据 下面分析具体代码,先看Varien_Data_Collection_Db::load()方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// Varien_Data_Collection_Db::load()
/**
 * Load data
 *
 * @param   bool $printQuery
 * @param   bool $logQuery
 *
 * @return  Varien_Data_Collection_Db
 */
public function load($printQuery = false, $logQuery = false)
{
    if ($this->isLoaded()) {
        return $this;
    }

    $this->_beforeLoad();

    $this->_renderFilters()
         ->_renderOrders()
         ->_renderLimit();

    $this->printLogQuery($printQuery, $logQuery);
    $data = $this->getData();
    $this->resetData();

    if (is_array($data)) {
        foreach ($data as $row) {
            $item = $this->getNewEmptyItem();
            if ($this->getIdFieldName()) {
                $item->setIdFieldName($this->getIdFieldName());
            }
            $item->addData($row);
            $this->addItem($item);
        }
    }

    $this->_setIsLoaded();
    $this->_afterLoad();
    return $this;
}

可以看出,Varien_Data_Collection_Db::load()方法里通过 $data = $this->getData() 获取数据。 再看 getData() 方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// Varien_Data_Collection_Db::getData()
/**
 * Get all data array for collection
 *
 * @return array
 */
public function getData()
{
    if ($this->_data === null) {
        $this->_renderFilters()
             ->_renderOrders()
             ->_renderLimit();
        $this->_data = $this->_fetchAll($this->_select);
        $this->_afterLoadData();
    }
    return $this->_data;
}

Varien_Data_Collection_Db::getData()方法里面又封装了一层,再来看 Varien_Data_Collection_Db::_fetchAll()方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Varien_Data_Collection_Db::_fetchAll()
/**
 * Fetch collection data
 *
 * @param   Zend_Db_Select $select
 * @return  array
 */
protected function _fetchAll($select)
{
    if ($this->_canUseCache()) {
        $data = $this->_loadCache($select);
        if ($data) {
            $data = unserialize($data);
        } else {
            $data = $this->getConnection()->fetchAll($select, $this->_bindParams);
            $this->_saveCache($data, $select);
        }
    } else {
        $data = $this->getConnection()->fetchAll($select, $this->_bindParams);
    }
    return $data;
}

可以看出,collection 是通过 Varien_Data_Collection_Db::_fetchAll() 来获取数据的,并且其逻辑和上面预测的逻辑几乎一致: 通过 Varien_Data_Collection_Db::_canUseCache() 判断是否使用缓存, 假如使用缓存,则通过 Varien_Data_Collection_Db::_loadCache() 方法获取缓存,取得数据后,检查数据是否为真,如果为真(缓存存在),则反序列化获取的字符串;如果数据为假(缓存不存在),则查询数据库,并将查询得到的数据保存为缓存; 假如不使用缓存,则直接查询数据库获取数据。 最后将取得的数据返回。

那么如何控制 collection 是否使用缓存? 具体逻辑在 Varien_Data_Collection_Db::_canUseCache()方法中:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
 * Check if cache can be used for collection data
 *
 * @return bool
 */
protected function _canUseCache()
{
    return $this->_getCacheInstance();
}

/**
 * Retrieve cache instance
 *
 * @return Zend_Cache_Core
 */
protected function _getCacheInstance()
{
    if (isset($this->_cacheConf['object'])) {
        return $this->_cacheConf['object'];
    }
    return false;
}

Varien_Data_Collection_Db::_canUseCache() 方法又调用了 Varien_Data_Collection_Db::_getCacheInstance() 方法。 其中逻辑是: 如果 Varien_Data_Collection_Db->_cacheConf[‘object’] 经过初始化,则返回此值;否则为假。 Varien_Data_Collection_Db->_cacheConf 是在 Varien_Data_Collection_Db::initCache() 方法中初始化的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
/**
 * Initialize collection cache
 *
 * @param $object
 * @param string $idPrefix
 * @param array $tags
 * @return Varien_Data_Collection_Db
 */
public function initCache($object, $idPrefix, $tags)
{
    $this->_cacheConf = array(
        'object'    => $object,
        'prefix'    => $idPrefix,
        'tags'      => $tags
    );
    return $this;
}

Varien_Data_Collection_Db::initCache() 是一个 public 方法,所以如果你有一个 collection 需要使用缓存,则只需要显示调用它的 initCache($object, $idPrefix, $tags) 方法,传入合适的参数即可。

Built with Hugo
主题 StackJimmy 设计