Help us understand the problem. What is going on with this article?

Doctrine DBALで取得したデータをキャッシュする

More than 3 years have passed since last update.

はじめに

例えば、都道府県のデータをデータベースに格納していて、単純に検索するだけの処理がある場合、検索結果はほぼ普遍的なわけで、都度データベースにクエリを投げるのではなく、同じクエリであれば、結果をキャッシュしたほうが、より効率的です。

Doctrine DBALの下記ページを参考に試しに、キャッシュしてみます。
http://doctrine-orm.readthedocs.org/projects/doctrine-dbal/en/latest/reference/caching.html

QueryCacheProfileの作成

キャッシュをどのように保存するか、どの程度キャッシュするかなど、キャッシュに関する設定を行います。

use Doctrine\Common\Cache\FilesystemCache;
use Doctrine\DBAL\Cache\QueryCacheProfile;

$cache = new FilesystemCache('/path/to/save');
$cacheProfile = new QueryCacheProfile(0, 'cache-key', $cache);

いろいろなcache

doctrine/cache には、いろいろなcacheクラスがあります。
https://github.com/doctrine/cache/tree/master/lib/Doctrine/Common/Cache

  • FilesystemCache
  • MongoDBCache
  • MemcachedCache
  • PhpFileCache
  • RedisCache

executeCacheQuery

executeQueryにて、QueryCacheProfileを指定すると、executeCacheQueryがコールされます。

https://github.com/doctrine/dbal/blob/master/lib/Doctrine/DBAL/Connection.php

/**
 * Executes a caching query.
 *
 * @param string                                 $query  The SQL query to execute.
 * @param array                                  $params The parameters to bind to the query, if any.
 * @param array                                  $types  The types the previous parameters are in.
 * @param \Doctrine\DBAL\Cache\QueryCacheProfile $qcp    The query cache profile.
 *
 * @return \Doctrine\DBAL\Driver\ResultStatement
 *
 * @throws \Doctrine\DBAL\Cache\CacheException
 */
public function executeCacheQuery($query, $params, $types, QueryCacheProfile $qcp)
{
    $resultCache = $qcp->getResultCacheDriver() ?: $this->_config->getResultCacheImpl();
    if ( ! $resultCache) {
        throw CacheException::noResultDriverConfigured();
    }

    list($cacheKey, $realKey) = $qcp->generateCacheKeys($query, $params, $types);

    // fetch the row pointers entry
    if ($data = $resultCache->fetch($cacheKey)) {
        // is the real key part of this row pointers map or is the cache only pointing to other cache keys?
        if (isset($data[$realKey])) {
            $stmt = new ArrayStatement($data[$realKey]);
        } elseif (array_key_exists($realKey, $data)) {
            $stmt = new ArrayStatement(array());
        }
    }

    if (!isset($stmt)) {
        $stmt = new ResultCacheStatement($this->executeQuery($query, $params, $types), $resultCache, $cacheKey, $realKey, $qcp->getLifetime());
    }

    $stmt->setFetchMode($this->defaultFetchMode);

    return $stmt;
}

closeCursor

キャッシュがない場合、ResultCacheStatement::closeCursorをコールすることで、結果セットをキャッシュすることができます。
https://github.com/doctrine/dbal/blob/master/lib/Doctrine/DBAL/Cache/ResultCacheStatement.php

public function closeCursor()
{
    $this->statement->closeCursor();
    if ($this->emptied && $this->data !== null) {
        $data = $this->resultCache->fetch($this->cacheKey);
        if ( ! $data) {
            $data = array();
        }
        $data[$this->realKey] = $this->data;

        $this->resultCache->save($this->cacheKey, $data, $this->lifetime);
        unset($this->data);
    }
}

実行結果

キャッシュがない場合、executeQueryは、データベースからデータを取得し、ResultCacheStatementを返却します。
キャッシュがある場合、executeQueryは、キャッシュからデータを取得し、ArrayStatementを返却します。

use Doctrine\DBAL\Cache\QueryCacheProfile;

public function findBySql($sql, $params = [], QueryCacheProfile $cacheProfile) {
    $statement = $this->connection->executeQuery($sql, $params, [], $cacheProfile);
    /* キャッシュがない場合 */
    /* @var $statement \Doctrine\DBAL\Cache\ResultCacheStatement */
    /* キャッシュがない場合 */
    /* @var $statement \Doctrine\DBAL\Cache\ArrayStatement */
    $result = $statement->fetchAll();
    $statement->closeCursor();
    return $result;
}

おわりに

Symfony Advent Calendarなのに、Doctrine/DBALの小ネタで大変恐縮ですが、実戦ですぐ使えそうなネタだったので、調査もかねて書いてみました。
これで、2年連続参加できました。
来年も懲りずに、参加しようと思います。
それでは、少し早いですが、よいお年を。

photocreate
ITの力で、写真を通じて感動があふれかえる社会を実現する「フォトライフ構想」を目指すWebサービスを展開しています
https://www.photocreate.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away