缓存
简介
Laravel 为各种缓存后端提供了丰富而统一的 API。缓存配置位于 config/cache.php
。在此文件中可以指定整个应用默认使用的缓存驱动。Laravel 支持流行的缓存后端,如开箱即用的 Memcached 和 Redis。
缓存配置文件还包含各种其它选项,这些选项都记录在文件中,因此请务必阅读这些选项。默认情况下,Laravel 配置使用 file
缓存驱动,此驱动将序列化后的缓存对象存储在文件系统中。对于大型应用,推荐使用更专业的驱动,如 Memcached 或 Redis。甚至还可以为同一个驱动配置多个缓存配置。
驱动前提
数据库
使用 database
缓存驱动时,需要创建一张数据表来包含缓存数据。下面是一个 Schema
声明的示例:
Schema::create('cache', function ($table) {
$table->string('key')->unique();
$table->text('value');
$table->integer('expiration');
});
也可以使用 Artisan 命令
php artisan cache:table
生成对应数据表的迁移。
Memcached
使用 Memcached 驱动时,要求安装 Memcached PECL 扩展包。可以在 config/cache.php
配置文件中列出所有的 Memcached 服务器:
'memcached' => [
[
'host' => '127.0.0.1',
'port' => 11211,
'weight' => 100
],
],
还可以将 host
选项设置为一个 UNIX socket 路径。如果这样配置,应将 port
选项设置为 0
:
'memcached' => [
[
'host' => '/var/run/memcached/memcached.sock',
'port' => 0,
'weight' => 100
],
],
Redis
使用 Laravel 的 Redis 缓存之前,需要使用 Composer 安装 predis/predis
扩展包(~1.0
)或使用 PECL 安装 PhpRedis PHP 扩展。
关于配置 Redis 的更多信息,请查看 Laravel 文档。
缓存使用
获取缓存实例
Illuminate\Contracts\Cache\Factory
和 Illuminate\Contracts\Cache\Repository
Contracts 提供访问 Laravel 缓存服务的方法。Factory
Contract 提供了访问应用中定义的所有缓存驱动的方法。Repository
Contract 通常是 cache
配置文件中指定的应用默认缓存驱动的实现。
不过,也可以使用 Cache
Facade,我们会在本文档中使用它。Cache
Facade 为访问底层 Laravel 缓存 Contracts 实现提供了方便简洁的方法:
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Cache;
class UserController extends Controller
{
/**
* 显示应用的所有用户列表
*
* @return Response
*/
public function index()
{
$value = Cache::get('key');
//
}
}
访问多个缓存存储
使用 Cache
Facade,可以通过 store
方法访问各种缓存存储。传递给 store
方法的键名应该和在 cache
配置文件的 stores
数组中列出的其中一项相对应:
$value = Cache::store('file')->get('foo');
Cache::store('redis')->put('bar', 'baz', 10);
从缓存获取数据
Cache
Facade 的 get
方法用来从缓存中获取数据。如果要获取的缓存项在缓存中不存在,会返回 null
。如果需要,还可以传递第二个参数给 get
方法,用于指定当缓存项不存在时返回的默认值:
$value = Cache::get('key');
$value = Cache::get('key', 'default');
甚至还可以传递一个 Closure
作为默认值。如果指定缓存项在缓存中不存在,那么会返回 Closure
的结果。传递闭包允许您后续从数据库或其它外部服务获取默认值:
$value = Cache::get('key', function () {
return DB::table(...)->get();
});
检查缓存项是否存在
has
方法可用于判断缓存项是否在缓存中存在。如果缓存项的值是 null
或 false
,此方法会返回 false
:
if (Cache::has('key')) {
//
}
增加/减少值
increment
和 decrement
方法可用于调整缓存中值为整数的缓存项。两个方法都接收一个可选的第二个参数表示缓存的值增加或减少的具体数量:
Cache::increment('key');
Cache::increment('key', $amount);
Cache::decrement('key');
Cache::decrement('key', $amount);
获取 & 存储
有时可能希望从缓存中获取缓存项,但还希望如果请求的缓存项不存在时存储默认值。例如,从缓存中获取所有用户,或者,当缓存不存在时,从数据库中获取所有用户,并将其添加到缓存中。可以使用 Cache::remember
方法完成此操作:
$value = Cache::remember('users', $minutes, function () {
return DB::table('users')->get();
});
如果缓存项在缓存中不存在,传递给 remember
方法的 Closure
会执行,并将其结果放到缓存中。
可以使用 rememberForever
方法从缓存中获取缓存项或将其永久存储到缓存:
$value = Cache::rememberForever('users', function () {
return DB::table('users')->get();
});
获取 & 删除
如果需要从缓存中获取缓存项并删除它,可以使用 pull
方法。与 get
方法一样,如果缓存项在缓存中不存在,会返回 null
:
$value = Cache::pull('key');
存储数据到缓存
可以使用 Cache
Facade 的 put
方法将数据存储到缓存中。将数据放到缓存时,需要指定数据缓存的分钟数:
Cache::put('key', 'value', $minutes);
除了传递整数值的分钟数之外,还可以传递表示缓存数据过期时间的 DateTime
实例:
$expiresAt = now()->addMinutes(10);
Cache::put('key', 'value', $expiresAt);
不存在时存储
add
只有在缓存项在缓存中不存在时,才会将其添加到缓存。如果缓存项实际添加到了缓存,会返回 true
。否则,返回 false
:
Cache::add('key', 'value', $minutes);
永久存储
forever
方法可以将缓存项永久存储到缓存中。由于这些数据不会过期,因此需要使用 forget
方法手动将其从缓存中移除:
Cache::forever('key', 'value');
如果使用 Memcached 驱动,「永久」存储的数据会在缓存达到最大大小限制时被移除。
从缓存删除数据
可以使用 forget
方法从缓存中移除数据:
Cache::forget('key');
可以使用 flush
方法清空所有缓存:
Cache::flush();
清空缓存不关心缓存前缀,而是从缓存中移除所有数据。因此在清空与其它应用共享的缓存时,请慎重考虑。
原子锁
要使用此功能,应用必须使用
memcached
或redis
作为默认的缓存驱动。此外,所有服务器必须和同一中央缓存服务器通信。
原子锁允许操作分布式锁,而无需担心竞争条件。例如,Laravel Forge 使用原子锁来确保一次只能在服务器上执行一个远程任务。可以使用 Cache::lock
方法创建和管理锁:
if (Cache::lock('foo', 10)->get()) {
// 获得锁 10 秒
Cache::lock('foo')->release();
}
get
方法还接收一个闭包。闭包执行后,Laravel 会自动释放锁:
Cache::lock('foo')->get(function () {
// 获得无期限锁并自动释放
});
如果请求时不能获得锁,可以指示 Laravel 等待指定秒数。如果在指定的时间限制内不能获得锁,会抛出一个 Illuminate\Contracts\Cache\LockTimeoutException
:
if (Cache::lock('foo', 10)->block(5)) {
// 最多等待 5 秒后获取锁
}
Cache::lock('foo', 10)->block(5, function () {
// 最多等待 5 秒后获取锁
});
缓存辅助函数
除了使用 Cache
Facade 或 Cache Contract,还可以使用全局辅助函数 cache
在缓存中获取和存储数据。当使用单个字符串参数调用 cache
函数时,会返回给定键的值:
$value = cache('key');
如果提供键/值对和一个过期时间给该函数,它会按指定的期限将值存储到缓存中:
cache(['key' => 'value'], $minutes);
cache(['key' => 'value'], now()->addSeconds(10));
如果不带任何参数调用 cache
函数,会返回一个 Illuminate\Contracts\Cache\Factory
实现的实例,允许您调用所有其它缓存方法:
cache()->remember('users', $minutes, function () {
return DB::table('users')->get();
});
当测试全局辅助函数
cache
时,可以使用Cache::shouldReceive
方法,就像 测试 Facade 一样。
缓存标签
使用
file
或database
缓存驱动时不支持缓存标签。此外,对「永久」存储的缓存使用多个标签时,类似memcached
驱动的性能会是最好的,它会自动清除旧的数据。
存储打标签的数据
缓存标签允许您在缓存中为相关的缓存项打上标签,然后清空所有指定了给定标签的缓存数据。可以通过传递有序排列的标签名称的数组获取打标签的缓存数据。例如,我们获取打标签的缓存数据并 put
值到缓存中:
Cache::tags(['people', 'artists'])->put('John', $john, $minutes);
Cache::tags(['people', 'authors'])->put('Anne', $anne, $minutes);
获取打标签的数据
要获取打标签的缓存项,传递同样有序排列的标签列表给 tags
方法,然后使用希望获取的键名调用 get
方法:
$john = Cache::tags(['people', 'artists'])->get('John');
$anne = Cache::tags(['people', 'authors'])->get('Anne');
删除打标签的数据
可以清空被指定了标签或标签列表的所有缓存项。例如,如下语句会移除所有打了 people
,authors
或者同时两个标签的缓存项。因此,Anne
和 John
两项都会从缓存中移除:
Cache::tags(['people', 'authors'])->flush();
相反,如下语句只会移除打了 authors
标签的缓存项,因此 Anne
会被移除,而 John
不会:
Cache::tags('authors')->flush();
添加自定义缓存驱动
编写驱动
创建自定义缓存驱动,首先要实现 Illuminate\Contracts\Cache\Store
Contract。因此,一个 MongoDB 缓存的实现应该看起来像这样:
namespace App\Extensions;
use Illuminate\Contracts\Cache\Store;
class MongoStore implements Store
{
public function get($key) {}
public function many(array $keys);
public function put($key, $value, $minutes) {}
public function putMany(array $values, $minutes);
public function increment($key, $value = 1) {}
public function decrement($key, $value = 1) {}
public function forever($key, $value) {}
public function forget($key) {}
public function flush() {}
public function getPrefix() {}
}
我们只需要使用 MongoDB 连接实现这些方法。至于实现这些方法的示例,可以在框架源代码中查看 Illuminate\Cache\MemcachedStore
。方法实现后,就可以注册驱动了。
Cache::extend('mongo', function ($app) {
return Cache::repository(new MongoStore);
});
如果想知道在哪里放自定义缓存驱动代码,可以在
app
目录中创建一个Extensions
命名空间。然而,Laravel 并不强制规定应用的结构,您可以根据自己的喜好自由组织应用。
注册驱动
要在 Laravel 中注册自定义驱动,我们会使用 Cache
Facade 的 extend
方法。可以在 Laravel 应用默认的 App\Providers\AppServiceProvider
文件的 boot
方法中调用 Cache::extend
来完成,或者创建一个自己的服务提供者来放置扩展代码 —— 只是不要忘了在 config/app.php
的服务提供者数组中注册该服务提供者:
namespace App\Providers;
use App\Extensions\MongoStore;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\ServiceProvider;
class CacheServiceProvider extends ServiceProvider
{
/**
* 注册后启动服务
*
* @return void
*/
public function boot()
{
Cache::extend('mongo', function ($app) {
return Cache::repository(new MongoStore);
});
}
/**
* 注册容器绑定
*
* @return void
*/
public function register()
{
//
}
}
传递给 extend
方法的第一个参数是驱动的名字。它对应于 config/cache.php
配置文件中的 driver
选项。第二个参数是一个应返回 Illuminate\Cache\Repository
实例的闭包。此闭包会传递给 $app
实例,它是一个 服务容器 的实例。
扩展注册后,将 config/cache.php
配置文件中的 driver
项更新为扩展的驱动名。
事件
如果要在每次缓存操作时执行代码,可以监听缓存触发的 事件。通常情况下,应该将这些事件监听器放在 EventServiceProvider
中:
/**
* 应用的事件监听器映射
*
* @var array
*/
protected $listen = [
'Illuminate\Cache\Events\CacheHit' => [
'App\Listeners\LogCacheHit',
],
'Illuminate\Cache\Events\CacheMissed' => [
'App\Listeners\LogCacheMissed',
],
'Illuminate\Cache\Events\KeyForgotten' => [
'App\Listeners\LogKeyForgotten',
],
'Illuminate\Cache\Events\KeyWritten' => [
'App\Listeners\LogKeyWritten',
],
];