快取系統
- 設定
- #驅動的前提條件
- 快取的使用
- 取得快取實例
- #從快取中取得資料
- #在快取中儲存資料
- 刪除快取中的資料
- 原子鎖定
- Cache 輔助函數
- 快取標記
- 寫入被標記的快取資料
- 存取被標記的快取資料
- 移除被標記的快取資料
- ##增加自訂快取驅動器
- 寫驅動程式
#設定
config/cache.php 檔案中。在該檔案中你可以指定應用程式預設使用哪個快取驅動。 Laravel 支援目前流行的後端緩存,例如 Memcached
和資料庫
當使用 database
快取驅動程式時,你需要設定一個表格來存放快取資料。以下是建立快取資料表結構的Schema
宣告範例:
Schema::create('cache', function ($table) { $table->string('key')->unique(); $table->text('value'); $table->integer('expiration'); });
#{tip} 你也可以使用Artisan 指令
php artisan cache:table
來生成合適的遷移。
Memcached
使用 Memcached 驅動程式需要安裝 Memcached PECL 擴充包 。你可以把所有的 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
在使用Laravel 的Redis在快取之前,你需要透過Composer 安裝predis/predis
擴充包(~1.0) 或使用PECL 安裝PhpRedis PHP 擴充功能。
如需了解更多關於 Redis 的配置,請參考 Laravel Redis 文件。
快取的使用
取得快取實例
Illuminate\Contracts\Cache\Factory
和Illuminate\Contracts\Cache\Repository
契約提供了Laravel 快取服務的存取機制。 Factory
契約為你的應用程式定義了存取所有快取驅動的機制。 Repository
契約通常是由你的 cache
設定檔指定的預設快取驅動實現的。
不過,你也可以使用 Cache
Facade,我們將在後續的文件中介紹。 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'); // } }
存取多個快取儲存
使用Cache
Facade,你可以透過store
方法來存取各種快取儲存。傳入store
方法的鍵應該對應cache
設定資訊檔中的stores
設定陣列中所列的儲存之一:
$value = Cache::store('file')->get('foo'); Cache::store('redis')->put('bar', 'baz', 600); // 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', $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 Facade 的put
方法將資料儲存到快取中:
如果快取的過期時間沒有傳遞給Cache::put('key', 'value', $seconds);
方法, 則快取將永久有效:
除了以整數形式傳遞過期時間的秒數,你還可以傳遞一個Cache::put('key', 'value');
# 實例來表示該資料的過期時間:Cache::put('key', 'value', now()->addMinutes(10));
方法將只儲存快取中不存在的資料。如果儲存成功,將傳回 true
,否則傳回 false
:Cache::add('key', 'value', $seconds);
資料永久儲存
forever
方法可用於持久化將資料儲存到快取中。因為這些資料不會過期,所以必須透過forget
方法從快取中手動刪除它們:
Cache::forever('key', 'value');
{tip} 如果你使用Memcached 驅動,當快取資料達到儲存上限時,「永久儲存」 的資料可能會被刪除。
刪除快取中的資料
你可以使用forget
方法從快取中刪除這些資料:
Cache::forget('key');
你也可以透過提供零或負的TTL 值來刪除這些資料:
Cache::put('key', 'value', 0); Cache::put('key', 'value', -5);
你可以使用flush
方法清空所有的緩存:
Cache::flush();
{note} 清空快取的方法並不會考慮快取前綴,會將快取中的所有內容刪除。因此在清除與其它應用程式共享的快取時,請慎重考慮。
原子鎖定
{note} 要使用這個特性,你的應用必須使用
memcached
,dynamodb
或redis
快取驅動程式作為你應用程式的預設快取驅動。此外,所有伺服器必須與同一中央快取伺服器進行通訊。
原子鎖定允許對分散式鎖定進行操作而不必擔心競爭條件。例如, Laravel Forge 使用原子鎖來確保在一台伺服器上每次只有一個遠端任務在執行。你可以使用 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 輔助函數
除了可以使用 Cache
Facade 或 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(); });
{tip} 如果在測試中使用全域輔助函數
cache
,你可以使用Cache::shouldReceive
方法就像測試Facade。
快取標記
{note} 快取標記不支援使用
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
契約。因此, 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); });
{tip} 如果你不知道要把快取驅動程式碼放在哪,你可以在
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
實例的閉包。這個閉包將傳遞一個 服務容器 的 $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', ], ];