[TOC]
# 缓存系统
## 配置
配置文件`config/cache.php`,默认使用 `file` 缓存驱动,支持多种驱动,可以为同一个驱动程序设置多个缓存配置。
### 驱动的前提条件
#### 数据库
使用 `database` 缓存驱动时,需要配置一个表来存放缓存数据。
```
Schema::create('cache', function ($table) {
$table->string('key')->unique();
$table->text('value');
$table->integer('expiration');
});
```
>[success] 可以使用 Artisan 命令 `php artisan cache:table` 来生成合适的迁移。
#### Memcached
使用 Memcached 驱动需要安装 [Memcached PECL 扩展包](https://pecl.php.net/package/memcached) ,设置配置文件`config/cache.php`
```
'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
使用 Redis 驱动需要通过 Composer 安装 `predis/predis` 扩展包 (~1.0) 或者使用 PECL 安装 PhpRedis PHP扩展。
## 缓存的使用
### 获取缓存实例
`Illuminate\Contracts\Cache\Factory` 和 `Illuminate\Contracts\Cache\Repository` [契约](/docs/{{version}}/contracts) 提供了 Laravel 缓存服务的访问机制。 `Factory` 契约为你的应用程序定义了访问所有缓存驱动的机制。 `Repository` 契约通常是由你的 `cache` 配置文件指定的默认缓存驱动实现的。
`Cache` Facade 为 Laravel 缓存契约底层的实现提供了方便又简洁的方法。
```
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Cache;
class UserController extends Controller
{
/**
* 展示应用的所有用户列表。
*
* @return Response
*/
public function index()
{
$value = Cache::get('key');
//
}
}
```
#### 访问多个缓存存储
通过 `store` 方法来访问在 `cache` 配置文件中的 `stores` 存储选项。
```
$value = Cache::store('file')->get('foo');
Cache::store('redis')->put('bar', 'baz', 600); // 10 分钟
```
### 从缓存中获取数据
通过 `get` 方法从缓存中获取数据。如果该数据在缓存中不存在,那么将返回 `null` 。使用 `get` 方法第二个参数设置默认值,第二个参数也可以使用闭包。
```
$value = Cache::get('key');
$value = Cache::get('key', 'default');
$value = Cache::get('key', function () {
return DB::table(...)->get();
});
```
#### 检查缓存项是否存在
>[info] 通过 `has` 方法判断缓存是否存在。如果为 `null` 或 `false` 则该方法将会返回 `false` 。
```
if (Cache::has('key')) {
//
}
```
#### 递增与递减值
通过 `increment` 和 `decrement` 方法调整缓存中整数的值。使用第二个可选参数设置递增或递减的步长。
```
Cache::increment('key');
Cache::increment('key', $amount);
Cache::decrement('key');
Cache::decrement('key', $amount);
```
#### 获取和存储
通过 `remember` 方法在未获取到缓存时,设置默认值,并存入缓存,第二参数为缓存时间秒。
```
$value = Cache::remember('users', $seconds, function () {
return DB::table('users')->get();
});
```
通过 `rememberForever` 方法从缓存中获取数据或者永久存储。
```
$value = Cache::rememberForever('users', function () {
return DB::table('users')->get();
});
```
#### 获取和删除
通过 `pull` 方法在获取缓存并删除,如果缓存不存在,则返回 `null`。
```
$value = Cache::pull('key');
```
### 在缓存中存储数据
通过 `put` 方法将数据存储到缓存中,未设置过期时间缓存永久有效。
```
Cache::put('key', 'value', $seconds); // 单位秒
Cache::put('key', 'value');
```
可以传递 `DateTime` 实例来表示该数据的过期时间。
```
Cache::put('key', 'value', now()->addMinutes(10));
```
#### 只存储没有的数据
通过 `add` 方法将只存储缓存中不存在的数据。如果存储成功,将返回 `true` ,否则返回 `false`。
```
Cache::add('key', 'value', $seconds);
```
#### 数据永久存储
通过 `forever` 方法将数据永久存储到缓存中,通过 `forget` 方法手动删除。
```
Cache::forever('key', 'value');
```
>[danger] 注意:如果你使用 Memcached 驱动,当缓存数据达到存储上限时,「永久存储」 的数据可能会被删除。
### 删除缓存中的数据
使用 `forget` 方法从缓存中删除数据。
```
Cache::forget('key');
```
将过期时间设置为零或者负数。
```
Cache::put('key', 'value', 0);
Cache::put('key', 'value', -5);
```
使用 `flush` 方法清空所有的缓存。
```
Cache::flush();
```
>[danger] 注意:清空缓存的方法并不会考虑缓存前缀,会将缓存中的所有内容删除。因此在清除与其它应用程序共享的缓存时,请慎重考虑。
### 原子锁
>[success] 要使用该特性,必须使用`memcached`或`dynamodb`或`redis`缓存驱动作为应用的默认缓存驱动。此外,所有服务器必须与同一中央缓存服务器进行通信。
原子锁允许对分布式锁进行操作而不必担心竞争条件。例如 [Laravel Forge](https://forge.laravel.com) 使用原子锁来确保在一台服务器上每次只有一个远程任务在执行。你可以使用 `Cache::lock` 方法来创建和管理锁。
```
use Illuminate\Support\Facades\Cache;
$lock = Cache::lock('foo', 10);
if ($lock->get()) {
//获取锁定10秒...
$lock->release();
}
```
`get` 方法也可以接收一个闭包。在闭包执行之后,Laravel 将会自动释放锁:
```
Cache::lock('foo')->get(function () {
// 获取无限期锁并自动释放...
});
```
如果在请求时锁无法使用,可以控制 Laravel 等待指定的秒数。如果在指定的时间限制内无法获取锁,则会抛出 `Illuminate\Contracts\Cache\LockTimeoutException` 错误。
```
use Illuminate\Contracts\Cache\LockTimeoutException;
$lock = Cache::lock('foo', 10);
try {
$lock->block(5);
// 等待最多5秒后获取的锁...
} catch (LockTimeoutException $e) {
// 无法获取锁...
} finally {
optional($lock)->release();
}
Cache::lock('foo', 10)->block(5, function () {
// 等待最多5秒后获取的锁...
});
```
#### 管理跨进程的锁
有时,你希望在一个进程中获取锁并在另外一个进程中释放它。例如,你可以在 Web 请求期间获取锁,并希望在该请求触发的队列作业结束时释放锁。在这种情况下,你应该将锁的作用域「owner token」传递给队列作业,以便作业可以使用给定的 token 重新实例化锁。
```
// 控制器里面...
$podcast = Podcast::find($id);
if ($lock = Cache::lock('foo', 120)->get()) {
ProcessPodcast::dispatch($podcast, $lock->owner());
}
// ProcessPodcast Job 里面...
Cache::restoreLock('foo', $this->owner)->release();
```
如果要释放锁而不考虑其当前所有者,可以使用 `forceRelease` 方法。
```
Cache::lock('foo')->forceRelease();
```
### Cache 辅助函数
```
// 获取缓存
$value = cache('key');
// 设置缓存
cache(['key' => 'value'], $seconds);
cache(['key' => 'value'], now()->addMinutes(10));
```
未设置参数的 `cache` 函数返回 `Illuminate\Contracts\Cache\Factory` 实例,可以调用其它缓存方法。
```
cache()->remember('users', $seconds, function () {
return DB::table('users')->get();
});
```
## 缓存标记
>[info] 缓存标记不支持使用 `file` 和 `database` 缓存驱动。此外,当使用多个缓存标记的缓存设置为「永久」时,类似 `memcached` 的缓存驱动性能最佳,它会自动清除旧的记录。
### 写入被标记的缓存数据
缓存标记允许你给缓存相关进行标记,以便后续清除这些缓存值。你可以通过传入标记名称的有序数组来访问标记的缓存。例如,我们可以使用标记的同时使用 `put` 方法设置缓存。
```
Cache::tags(['people', 'artists'])->put('John', $john, $seconds);
Cache::tags(['people', 'authors'])->put('Anne', $anne, $seconds);
```
### 访问被标记的缓存数据
若要获取一个被标记的缓存数据,请将相同的有序标记数组传递给 `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` [契约](/docs/{{version}}/contracts)。因此, MongoDB 的缓存实现看起来会像这样:
```
<?php
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, $seconds) {}
public function putMany(array $values, $seconds);
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);
});
```
>[success] 如果你不知道将缓存驱动代码放在哪,你可以在 `app` 目录下创建一个 `Extensions` 命名空间。然而,Laravel 并没有硬性规定应用程序的结构,你可以根据自己的喜好自由组织你的应用程序。
### 注册驱动
要使用 Laravel 来注册自定义缓存驱动,就要在 `Cache` Facade 上使用 `extend` 方法。 对 `Cache::extend` 的调用可以在新的 Laravel 应用程序中自带的 `App\Providers\AppServiceProvider` 的 `boot` 方法中完成,或者你也可以创建自己的服务提供者来存放扩展,只是不要忘记在 `config/app.php` 的 provider 数组中注册服务提供者:
```
<?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` 实例的闭包。该闭包将传递一个 [服务容器](/docs/{{version}}/container) 的 `$app` 实例。
一旦你的扩展程序注册后,需要将 `config/cache.php` 配置文件中的 `driver` 选项更新为你的扩展名称。
## 事件
>[info] 要在每次缓存操作时执行代码,你可以监听缓存触发的 [事件](/docs/{{version}}/events) 。通常,你应该将这些事件监听器放在 `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',
],
];
```
- 入门指南
- 安装
- 部署
- 基础功能
- 路由
- 中间件
- CSRF 保护
- 控制器
- 请求
- 响应
- 视图
- URL
- Session
- 表单验证
- 错误
- 日志
- 前端开发
- Blade 模板
- 本地化
- 脚手架
- 编译资源 Mix
- 安全相关
- 用户认证
- API 认证
- 综合话题
- 命令行
- 广播
- 缓存
- 集合
- 事件
- 文件存储
- 辅助函数
- 邮件发送
- 消息通知
- 扩展包开发
- 队列
- 任务调度
- 数据库
- 快速入门
- 查询构造器
- 分页
- 数据库迁移
- 数据填充
- Redis
- Eloquent ORM
- 快速入门
- 速查表
- Artisan
- Auth
- Blade
- Cache
- Collection
- Composer
- Config
- Container
- Cookie
- DB
- Environment
- Event
- File
- Helper
- Input
- Lang
- Log
- Model
- Pagination
- Queue
- Redirect
- Request
- Response
- Route
- SSH
- Schema
- Security
- Session
- Storage
- String
- URL
- UnitTest
- Validation
- View