[ Laravel 5.8 文件 ] 進階系列 —— 快取
配置
Laravel 為不同的快取系統提供了統一的 API。快取配置位於config/cache.php
。在該檔案中你可以指定在應用中預設使用哪個快取驅動。Laravel 開箱支援主流的快取後端如Memcached
和Redis
等。
快取配置檔案還包含其他文件化的選項,確保仔細閱讀這些選項。預設情況下,Laravel 被配置成使用檔案快取(file
驅動),這會將序列化資料和快取物件儲存到檔案系統。對於大型應用,建議使用記憶體快取如 Memcached 或 APC,你甚至可以為同一驅動配置多個快取配置。
驅動預備知識
資料庫
使用database
快取驅動時,你需要設定一張表儲存快取項。下面是該表的Schema
宣告:
Schema::create('cache', function($table) { $table->string('key')->unique(); $table->text('value'); $table->integer('expiration'); });
注:你還可以使用 Artisan 命令php artisan cache:table
通過相應的 schema 生成遷移。
Memcached
使用 Memcached 快取要求安裝了Memcached PECL 包
,即 PHP Memcached 擴充套件。你可以在配置檔案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)。
想要了解更多關於 Redis 的配置,檢視 Larave 的Redis 文件。
快取使用
獲取快取例項
Illuminate\Contracts\Cache\Factory
和Illuminate\Contracts\Cache\Repository
契約提供了訪問 Laravel 快取服務的方法。Factory
契約提供了所有訪問應用定義的快取驅動的方法。Repository
契約通常是應用中cache
配置檔案中指定的預設快取驅動的一個實現。
不過,你還可以使用Cache
門面,這也是我們在整個文件中使用的方式,Cache
門面提供了簡單方便的方式對底層 Laravel 快取契約實現進行訪問:
<?php namespace App\Http\Controllers; use Illuminate\Support\Facades\Cache; class UserController extends Controller { /** * 顯示應用所有使用者列表. * * @return Response */ public function index() { $value = Cache::get('key'); // } }
訪問多個快取儲存
使用Cache
門面,你可以使用store
方法訪問不同的快取儲存器,傳入store
方法的鍵就是cache
配置檔案中stores
配置數組裡列出的相應的儲存器:
$value = Cache::store('file')->get('foo'); Cache::store('redis')->put('bar', 'baz', 600);// 10分鐘
從快取中獲取資料
Cache
門面的get
方法用於從快取中獲取快取項,如果快取項不存在,返回null
。如果需要的話你可以傳遞第二個引數到get
方法指定快取項不存在時返回的自定義預設值:
$value = Cache::get('key'); $value = Cache::get('key', 'default');
你甚至可以傳遞一個閉包作為預設值,如果快取項不存在的話閉包的結果將會被返回。傳遞閉包允許你可以從資料庫或其它外部服務獲取預設值:
$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', $seconds, function() { return DB::table('users')->get(); });
如果快取項不存在,傳遞給remember
方法的閉包被執行並且將結果存放到快取中。
你還可以使用rememberForever
方法從快取中獲取資料或者將其永久儲存起來:
$value = Cache::rememberForever('users', function() { return DB::table('users')->get(); });
獲取 & 刪除
如果你需要從快取中獲取快取項然後刪除,你可以使用pull
方法,和get
方法一樣,如果快取項不存在的話返回null
:
$value = Cache::pull('key');
在快取中儲存資料
你可以使用Cache
門面上的put
方法在快取中儲存資料。當你在快取中儲存資料的時候,需要指定資料被快取的時間(秒數):
Cache::put('key', 'value', $seconds);
如果沒有傳遞快取時間到put
方法,則快取項永久有效:
Cache::put('key', 'value');
除了傳遞快取項失效時間,你還可以傳遞一個代表快取項有效時間的 PHPDatetime
例項:
$expiresAt = Carbon::now()->addMinutes(10); Cache::put('key', 'value', $expiresAt);
快取不存在時儲存資料
add
方法只會在快取項不存在的情況下新增資料到快取,如果資料被成功新增到快取返回true
,否則,返回false
:
Cache::add('key', 'value', $seconds);
永久儲存資料
forever
方法用於持久化儲存資料到快取,這些值必須通過forget
方法手動從快取中移除:
Cache::forever('key', 'value');
注:如果你使用的是 Memcached 驅動,當快取資料達到上限後永久儲存的資料就會被移除。
從快取中移除資料
你可以使用Cache
門面上的forget
方法從快取中移除快取項資料:
Cache::forget('key');
還可以通過設定快取有效期為 0 或負數來移除快取項:
Cache::put('key', 'value', 0); Cache::put('key', 'value', -5);
如果要清除所有快取,可以通過flush
方法:
Cache::flush();
注:清除快取並不管什麼快取鍵字首,而是從快取系統中移除所有資料,所以在使用這個方法時如果其他應用與本應用有共享快取時需要格外注意。
原子鎖
注:要使用這個功能,應用必須使用memcached
、dynamodb
或redis
快取驅動作為應用預設的快取驅動,此外,所有伺服器必須和同一臺中央快取伺服器進行通訊。
原子鎖允許你對分散式鎖進行操作而不必擔心競爭條件,例如,Laravel Forge
使用原子鎖來確保在一臺伺服器上同時只有一個遠端任務在執行,你可以通過Cache::lock
方法來建立和管理鎖:
if (Cache::lock('foo', 10)->get()) { // Lock acquired for 10 seconds... Cache::lock('foo')->release(); }
get
方法還可以接收一個閉包,在閉包執行之後,Laravel 會自動釋放鎖:
Cache::lock('foo')->get(function () { // Lock acquired indefinitely and automatically released... });
如果鎖在你請求的時候無效,可以告知 Laravel 等待直到鎖有效。如果鎖在指定限制時間內無法獲取,會丟擲Illuminate\Contracts\Cache\LockTimeoutException
異常:
use Illuminate\Contracts\Cache\LockTimeoutException; $lock = Cache::lock('foo', 10); try { $lock->block(5); // Lock acquired after waiting maximum of 5 seconds... } catch (LockTimeoutException $e) { // Unable to acquire lock... } finally { optional($lock)->release(); } Cache::lock('foo', 10)->block(5, function () { // Lock acquired after waiting maximum of 5 seconds... });
管理跨程序的鎖
有時候,你可能想要在一個程序中獲取鎖,在另一個程序中釋放鎖。例如,你可以在 Web 請求期間獲取鎖,然後在該請求觸發的某個佇列任務最後釋放這把鎖。在這種場景下,你應該傳遞鎖的域「」
快取輔助函式
除了使用Cache
門面或快取契約,還可以使用全域性的cache
函式來通過快取獲取和儲存資料。當帶有一個字串引數的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(); });
測試呼叫cache
函式時,可以像測試門面一樣使用Cache::shouldReceive
方法。
快取標籤
注:快取標籤目前不支援file
或database
快取驅動,此外,當使用多標籤的快取被設定為永久儲存時,使用memcached
驅動的快取有著最佳效能表現,因為 Memcached 會自動清除陳舊記錄。
儲存被打上標籤的快取項
快取標籤允許你給相關快取項打上同一個標籤以便於後續清除這些快取值,被打上標籤的快取可以通過傳遞一個被排序的標籤陣列來訪問。例如,我們可以通過以下方式在新增快取的時候設定標籤:
Cache::tags(['people', 'artists'])->put('John', $john, $seconds); Cache::tags(['people', 'authors'])->put('Anne', $anne, $seconds);
訪問被打上標籤的快取項
要獲取被打上標籤的快取項,傳遞同樣的有序標籤陣列到tags
方法然後使用你想要獲取的key來呼叫get
方法:
$john = Cache::tags(['people', 'artists'])->get('John'); $anne = Cache::tags(['people', 'authors'])->get('Anne');
移除被打上標籤的資料項
你可以同時清除被打上同一標籤/標籤列表的所有快取項,例如,以下語句會移除被打上people
或authors
標籤的所有快取:
Cache::tags(['people', 'authors'])->flush();
這樣,上面設定的Anne
和John
快取項都會從快取中移除。
相反,以下語句只移除被打上authors
標籤的語句,所以只有Anne
會被移除而John
不會:
Cache::tags('authors')->flush();
新增自定義快取驅動
編寫驅動
要建立自定義的快取驅動,首先需要實現Illuminate\Contracts\Cache\Store
契約,所以,我們的 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 連線來實現其中的每一個方法,想要看如何實現每個方法的示例,可以參考 Laravel 底層原始碼Illuminate\Cache\MemcachedStore
,實現完成後,我們就可以完成自定義驅動註冊:
Cache::extend('mongo', function($app) { return Cache::repository(new MongoStore); });
注:如果你在擔心將自定義快取驅動程式碼放到哪,可以在app
目錄下建立一個Extensions
名稱空間。不過,記住 Laravel 並沒有一個嚴格的應用目錄結構,你可以基於你的需要自由的組織目錄結構。
註冊驅動
要通過 Laravel 註冊自定義的快取驅動,可以使用Cache
門面上的extend
方法。對Cache::extend
的呼叫可以在 Laravel 自帶的App\Providers\AppServiceProvider
提供的boot
方法中完成,或者,你也可以建立自己的服務提供者來存放擴充套件——只是別忘了在配置檔案config/app.php
中註冊服務提供者到providers
陣列:
<?php namespace App\Providers; use App\Extensions\MongoStore; use Illuminate\Support\Facades\Cache; use Illuminate\Support\ServiceProvider; class CacheServiceProvider extends ServiceProvider { /** * Perform post-registration booting of services. * * @return void * @translator laravelacademy.org */ public function boot() { Cache::extend('mongo', function($app) { return Cache::repository(new MongoStore); }); } /** * Register bindings in the container. * * @return void */ public function register() { // } }
傳遞給extend
方法的第一個引數是驅動名稱。該值對應配置檔案config/cache.php
中的driver
選項。第二個引數是返回Illuminate\Cache\Repository
例項的閉包。該閉包中被傳入一個$app
例項,也就是服務容器的一個例項。
擴充套件被註冊後,只需簡單更新配置檔案config/cache.php
的driver
選項為自定義副檔名稱即可。
快取事件
要在每次快取操作時執行程式碼,你可以監聽快取觸發的事件,通常,你可以將這些快取處理器程式碼放到EventServiceProvider
中:
/** * The event listener mappings for the application. * * @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', ], ];