[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