通過緩存構建高性能 Laravel 應用
由 學院君 創建于3年前, 最后更新于 11個月前
版本號 #2
20678 views
9 likes
1 collects
配置
Laravel 為不同的緩存系統提供了統一的 API。緩存配置位于 config/cache.php。在該文件中你可以指定在應用中默認使用哪個緩存驅動。Laravel 開箱支持主流的緩存后端如 Memcached和 Redis 等。
緩存配置文件還包含其他文檔化的選項,確保仔細閱讀這些選項。默認情況下,Laravel 被配置成使用文件緩存,這會將序列化數據和緩存對象存儲到文件系統。對于大型應用,建議使用內存緩存如 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 緩存契約實現進行訪問:
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', 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', $minutes, 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', $minutes);
除了傳遞緩存項失效時間,你還可以傳遞一個代表緩存項有效時間的 PHP Datetime 實例:
$expiresAt = Carbon::now()->addMinutes(10);
Cache::put('key', 'value', $expiresAt);
緩存不存在時存儲數據
add 方法只會在緩存項不存在的情況下添加數據到緩存,如果數據被成功添加到緩存返回 true,否則,返回false:
Cache::add('key', 'value', $minutes);
永久存儲數據
forever 方法用于持久化存儲數據到緩存,這些值必須通過 forget 方法手動從緩存中移除:
Cache::forever('key', 'value');
注:如果你使用的是 Memcached 驅動,當緩存數據達到上限后永久存儲的數據就會被移除。
從緩存中移除數據
你可以使用 Cache 門面上的 forget 方法從緩存中移除緩存項數據:
Cache::forget('key');
還可以使用 flush 方法清除所有緩存:
Cache::flush();
注:清除緩存并不管什么緩存鍵前綴,而是從緩存系統中移除所有數據,所以在使用這個方法時如果其他應用與本應用有共享緩存時需要格外注意。
緩存輔助函數
除了使用 Cache 門面或緩存契約,還可以使用全局的 cache 函數來通過緩存獲取和存儲數據。當帶有一個字符串參數的 cache 函數被調用時,會返回給定鍵對應的緩存值(取值):
$value = cache('key');
如果你提供了鍵值對數組和一個過期時間給該函數,則會在指定的有效期內存儲緩存值(存儲):
cache(['key' => 'value'], $minutes);
cache(['key' => 'value'], Carbon::now()->addSeconds(10));
測試調用 cache 函數時,可以像測試門面一樣使用 Cache::shouldReceive 方法。
緩存標簽
注:緩存標簽目前不支持 file 或 database 緩存驅動,此外,當使用多標簽的緩存被設置為永久存儲時,使用 memcached 驅動的緩存有著最佳性能表現,因為 Memcached 會自動清除陳舊記錄。
存儲被打上標簽的緩存項
緩存標簽允許你給相關緩存項打上同一個標簽以便于后續清除這些緩存值,被打上標簽的緩存可以通過傳遞一個被排序的標簽數組來訪問。例如,我們可以通過以下方式在添加緩存的時候設置標簽:
Cache::tags(['people', 'artists'])->put('John', $john, $minutes);
Cache::tags(['people', 'authors'])->put('Anne', $anne, $minutes);
你可以給多個緩存項打上相同標簽,這是沒有數目限制的。
訪問被打上標簽的緩存項
要獲取被打上標簽的緩存項,傳遞同樣的有序標簽數組到 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 緩存實現看起來會像這樣子:
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 連接來實現其中的每一個方法,想要看如何實現每個方法的示例,可以參考 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 數組:
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',
],
];