到 GitHub 上改进本页面

数据缓存

数据缓存是指将一些 PHP 变量存储到缓存中,使用时再从缓存中取回。 它也是更高级缓存特性的基础,例如查询缓存内容缓存

如下代码是一个典型的数据缓存使用模式。 其中 $cache 指向缓存组件

// 尝试从缓存中取回 $data 
$data = $cache->get($key);

if ($data === false) {

    // $data 在缓存中没有找到,则重新计算它的值

    // 将 $data 存放到缓存供下次使用
    $cache->set($key, $data);
}

// 这儿 $data 可以使用了。

从2.0.11版本开始, 缓存组件 提供了getOrSet() 方法来简化数据的取回、计算和存储。下面的代码逻辑和上一个例子是完全一样的:

$data = $cache->getOrSet($key, function () {
    return $this->calculateSomething();
});

当缓存中有关联$key的数据时,将返回这个缓存的值。否则就执行匿名函数来计算出将要缓存的数据并返回它。 如果匿名函数需要作用域外的数据时,可以使用use语句把这些数据传递到匿名函数中。例如:

$user_id = 42;
$data = $cache->getOrSet($key, function () use ($user_id) {
    return $this->calculateSomething($user_id);
});

注意:getOrSet() 方法也支持缓存持续性和缓存依赖。请看缓存过期缓存依赖 来了解详细信息。

缓存组件

数据缓存需要缓存组件提供支持,它代表各种缓存存储器, 例如内存,文件,数据库。

缓存组件通常注册为应用程序组件,这样它们就可以 在全局进行配置与访问。如下代码演示了如何配置应用程序组件 cache 使用两个 memcached 服务器:

'components' => [
    'cache' => [
        'class' => 'yii\caching\MemCache',
        'servers' => [
            [
                'host' => 'server1',
                'port' => 11211,
                'weight' => 100,
            ],
            [
                'host' => 'server2',
                'port' => 11211,
                'weight' => 50,
            ],
        ],
    ],
],

然后就可以通过 Yii::$app->cache 访问上面的缓存组件了。

由于所有缓存组件都支持同样的一系列 API ,并不需要修改使用缓存的业务代码 就能直接替换为其他底层缓存组件,只需在应用配置中重新配置一下就可以。 例如,你可以将上述配置修改为使用 APC cache:

'components' => [
    'cache' => [
        'class' => 'yii\caching\ApcCache',
    ],
],

提示:你可以注册多个缓存组件,很多依赖缓存的类默认调用 名为 cache 的组件(例如 yii\web\UrlManager)。

支持的缓存存储器

Yii 支持一系列缓存存储器,概况如下:

  • yii\caching\ApcCache:使用 PHP APC 扩展。 这个选项可以认为是集中式应用程序环境中 (例如:单一服务器,没有独立的负载均衡器等)最快的缓存方案。
  • yii\caching\DbCache:使用一个数据库的表存储缓存数据。要使用这个缓存, 你必须创建一个与 yii\caching\DbCache::$cacheTable 对应的表。
  • 这个组件的目的是为了简化那些需要查询缓存有效性的代码。例如, 在开发中如果服务器没有实际的缓存支持,用它配置一个缓存组件。 一个真正的缓存服务启用后,可以再切换为使用相应的缓存组件。 两种条件下你都可以使用同样的代码 Yii::$app->cache->get($key) 尝试从缓存中取回数据而不用担心 Yii::$app->cache 可能是 null

  • yii\caching\FileCache:使用标准文件存储缓存数据。 这个特别适用于缓存大块数据,例如一个整页的内容。
  • yii\caching\MemCache:使用 PHP memcachememcached 扩展。 这个选项被看作分布式应用环境中(例如:多台服务器,有负载均衡等) 最快的缓存方案。
  • yii\redis\Cache:实现了一个基于 Redis 键值对存储器的缓存组件 (需要 redis 2.6.12 及以上版本的支持 )。
  • yii\caching\WinCache:使用 PHP WinCache另可参考)扩展.
  • yii\caching\XCache:使用 PHP XCache扩展。
  • yii\caching\ZendDataCache:使用 [Zend Data Cache](http://files.zend.com/help/Zend-Server-6/zend- server.htm#data_cache_component.htm) 作为底层缓存媒介。

提示:你可以在同一个应用程序中使用不同的缓存存储器。一个常见的策略是使用基于内存的缓存存储器 存储小而常用的数据(例如:统计数据),使用基于文件或数据库的缓存存储器 存储大而不太常用的数据(例如:网页内容)。

缓存 API

所有缓存组件都有同样的基类 yii\caching\Cache ,因此都支持如下 API:

  • get():通过一个指定的键(key)从缓存中取回一项数据。 如果该项数据不存在于缓存中或者已经过期/失效,则返回值 false。
  • set():将一个由键指定的数据项存放到缓存中。
  • add():如果缓存中未找到该键,则将指定数据存放到缓存中。
  • getOrSet():返回由键指定的缓存项,或者执行回调函数,把函数的返回值用键来关联存储到缓存中,最后返回这个函数的返回值。
  • multiGet():由指定的键获取多个缓存数据项。
  • multiSet():一次存储多个数据项到缓存中,每个数据都由一个键来指明。
  • multiAdd():一次存储多个数据项到缓存中,每个数据都由一个键来指明。如果某个键已经存在,则略过该数据项不缓存。
  • exists():返回一个值,指明某个键是否存在于缓存中。
  • delete():通过一个键,删除缓存中对应的值。
  • flush():删除缓存中的所有数据。

注意:千万别直接用false布尔值当做数据项缓存,因为get()方法用 false作为返回值来表名对应的缓存项不存在。你可以把false放到一个数组里然后缓存这个数组来避免上述的混淆问题。

有些缓存存储器如 MemCache,APC 支持以批量模式取回缓存值,这样可以节省取回缓存数据的开支。 multiGet()multiAdd() API提供对该特性的支持。 如果底层缓存存储器不支持该特性,Yii 也会模拟实现。

由于 yii\caching\Cache 实现了 PHP ArrayAccess 接口, 缓存组件也可以像数组那样使用,下面是几个例子:

$cache['var1'] = $value1;  // 等价于: $cache->set('var1', $value1);
$value2 = $cache['var2'];  // 等价于: $value2 = $cache->get('var2');

缓存键

存储在缓存中的每项数据都通过键作唯一识别。 当你在缓存中存储一项数据时,必须为它指定一个键, 稍后从缓存中取回数据时,也需要提供相应的键。

你可以使用一个字符串或者任意值作为一个缓存键。当键不是一个字符串时, 它将会自动被序列化为一个字符串。

定义一个缓存键常见的一个策略就是在一个数组中包含所有的决定性因素。 例如,yii\db\Schema 使用如下键存储一个数据表的结构信息。

[
    __CLASS__,              // 结构类名
    $this->db->dsn,         // 数据源名称
    $this->db->username,    // 数据库登录用户名
    $name,                  // 表名
];

如你所见,该键包含了可唯一指定一个数据库表所需的所有必要信息。

注意:通过multiSet()或者multiAdd()方法缓存的数据项的键,它的类型只能是字符串或整型, 如果你想使用较为复杂的键,可以通过 set()或者add()方法来存储。

当同一个缓存存储器被用于多个不同的应用时,应该为每个应用指定一个唯一的缓存键前缀以避免缓存键冲突。 可以通过配置 yii\caching\Cache::$keyPrefix 属性实现。 例如,在应用配置中可以编写如下代码:

'components' => [
    'cache' => [
        'class' => 'yii\caching\ApcCache',
        'keyPrefix' => 'myapp',       // 唯一键前缀
    ],
],

为了确保互通性,此处只能使用字母和数字。

缓存过期

默认情况下,缓存中的数据会永久存留,除非它被某些缓存策略强制移除(例如:缓存空间已满,最老的数据会被移除)。 要改变此特性,你可以在调用 set() 存储一项数据时提供一个过期时间参数。 该参数代表这项数据在缓存中可保持有效多少秒。 当你调用 get() 取回数据时, 如果它已经过了超时时间,该方法将返回 false,表明在缓存中找不到这项数据。 例如:

// 将数据在缓存中保留 45 秒
$cache->set($key, $data, 45);

sleep(50);

$data = $cache->get($key);
if ($data === false) {
    // $data 已过期,或者在缓存中找不到
}

从2.0.11开始,如果想自定义缓存的持续时间,你可以在缓存组件配置中设置defaultDuration成员属性的值。 这样设置会覆盖默认的缓存持续时间,且在使用set()方法时不必每次都传递$duration参数。

缓存依赖

除了超时设置,缓存数据还可能受到缓存依赖的影响而失效。 例如,yii\caching\FileDependency 代表对一个文件修改时间的依赖。 这个依赖条件发生变化也就意味着相应的文件已经被修改。 因此,缓存中任何过期的文件内容都应该被置为失效状态, 对 get() 的调用都应该返回 false。

缓存依赖用 yii\caching\Dependency 的派生类所表示。 当调用 set() 在缓存中存储一项数据时, 可以同时传递一个关联的缓存依赖对象。例如:

// 创建一个对 example.txt 文件修改时间的缓存依赖
$dependency = new \yii\caching\FileDependency(['fileName' => 'example.txt']);

// 缓存数据将在30秒后超时
// 如果 example.txt 被修改,它也可能被更早地置为失效状态。
$cache->set($key, $data, 30, $dependency);

// 缓存会检查数据是否已超时。
// 它还会检查关联的依赖是否已变化。
// 符合任何一个条件时都会返回 false。
$data = $cache->get($key);

下面是可用的缓存依赖的概况:

注意:避免对带有缓存依赖的缓存项使用 exists() 方法,因为它不检测缓存依赖(如果有的话)是否有效,所以调用get()可能返回false而调用exists()却返回true

查询缓存

查询缓存是一个建立在数据缓存之上的特殊缓存特性。 它用于缓存数据库查询的结果。

查询缓存需要一个 数据库连接 和一个有效的 cache 应用组件。 查询缓存的基本用法如下,假设 $db 是一个 yii\db\Connection 实例:

$result = $db->cache(function ($db) {

    // the result of the SQL query will be served from the cache
    // if query caching is enabled and the query result is found in the cache
    return $db->createCommand('SELECT * FROM customer WHERE id=1')->queryOne();

});

查询缓存可以用在DAOActiveRecord上:

$result = Customer::getDb()->cache(function ($db) {
    return Customer::find()->where(['id' => 1])->one();
});

信息:有些 DBMS (例如:MySQL) 也支持数据库服务器端的查询缓存。 你可以选择使用任一查询缓存机制。 上文所述的查询缓存的好处在于你可以指定更灵活的缓存依赖因此可能更加高效。

缓存冲刷

当你想让所有的缓存数据失效时,可以调用yii\caching\Cache::flush()

冲刷缓存数据,你还可以从控制台调用yii cache/flush

  • yii cache: 列出应用中可用的缓存组件
  • yii cache/flush cache1 cache2: 冲刷缓存组件cache1, cache2 (可以传递多个用空格分开的缓存组件)
  • yii cache/flush-all: 冲刷应用中所有的缓存组件

信息:默认情况下,控制台应用使用独立的配置文件。所以,为了上述命令发挥作用,请确保Web应用和控制台应用配置相同的缓存组件。

配置

Query caching has three global configurable options through yii\db\Connection:

  • enableQueryCache: whether to turn on or off query caching. It defaults to true. Note that to effectively turn on query caching, you also need to have a valid cache, as specified by queryCache.
  • queryCacheDuration: this represents the number of seconds that a query result can remain valid in the cache. You can use 0 to indicate a query result should remain in the cache forever. This property is the default value used when yii\db\Connection::cache() is called without specifying a duration.
  • queryCache: 缓存应用组件的 ID。默认为 'cache'。 只有在设置了一个有效的缓存应用组件时,查询缓存才会有效。

Usages

You can use yii\db\Connection::cache() if you have multiple SQL queries that need to take advantage of query caching. The usage is as follows,

$duration = 60;     // cache query results for 60 seconds.
$dependency = ...;  // optional dependency

$result = $db->cache(function ($db) {

    // ... perform SQL queries here ...

    return $result;

}, $duration, $dependency);

Any SQL queries in the anonymous function will be cached for the specified duration with the specified dependency. If the result of a query is found valid in the cache, the query will be skipped and the result will be served from the cache instead. If you do not specify the $duration parameter, the value of queryCacheDuration will be used instead.

  • enableQueryCache:是否开启或关闭查询缓存,默认是true。注意,为了有效的开启查询缓存,你还需要配置一个由queryCache属性指定的缓存组件。
  • queryCacheDuration: 这表示查询结果能够保持有效的秒数,设置为0的话,则会在缓存组件中永久缓存。这也是调用yii\db\Connection::cache()而没有指明缓存持续时间时的默认值。
  • queryCache: 缓存应用组件的 ID。默认为 'cache'。 只有在设置了一个有效的缓存应用组件时,查询缓存才会有效。

用法

如果一次有多个SQL查询想利用查询缓存,你可以用yii\db\Connection::cache() 。用法如下,

$duration = 60;     // cache query results for 60 seconds.
$dependency = ...;  // optional dependency

$result = $db->cache(function ($db) {

    // ... perform SQL queries here ...

    return $result;

}, $duration, $dependency);

在匿名函数里的任何一个SQL查询都会伴随指定的duration和dependency来生成缓存。 如果一个SQL查询的结果在缓存中有效,那么这个SQl语句将会被跳过而它的查询结果会直接从缓存中读取。如果你没有指明$duration参数,那么使用queryCacheDuration属性。

有时在cache()里,你可能不想缓存某些特殊的查询,这时你可以用yii\db\Connection::noCache()

$result = $db->cache(function ($db) {

    // SQL queries that use query caching

    $db->noCache(function ($db) {

        // SQL queries that do not use query caching

    });

    // ...

    return $result;
});

如果仅仅想缓存一个单独的查询,那么在建立查询命令时可以使用yii\db\Command::cache()。例如,

// use query caching and set query cache duration to be 60 seconds
$customer = $db->createCommand('SELECT * FROM customer WHERE id=1')->cache(60)->queryOne();

也可以用yii\db\Command::noCache()来单独设置某些查询命令不缓存。例如,

$result = $db->cache(function ($db) {

    // SQL queries that use query caching

    // do not use query caching for this command
    $customer = $db->createCommand('SELECT * FROM customer WHERE id=1')->noCache()->queryOne();

    // ...

    return $result;
});

限制条件

当查询结果中含有资源句柄时,查询缓存无法使用。 例如,在有些 DBMS 中使用了 BLOB 列的时候, 缓存结果会为该数据列返回一个资源句柄。

有些缓存存储器有大小限制。例如,memcache 限制每条数据最大为 1MB。 因此,如果查询结果的大小超出了该限制, 则会导致缓存失败。