Cache
Introduction
您的應用程式執行的某些資料檢索或處理任務可能需要大量的 CPU 資源或花費數秒才能完成。在這種情況下,通常會將檢索到的資料快取一段時間,以便在後續請求相同資料時可以快速檢索。快取的資料通常儲存在非常快速的資料儲存區中,例如 Memcached 或 Redis。
值得慶幸的是,Laravel 為各種快取後端提供了具表現力且統一的 API,讓您可以利用它們極快的資料檢索速度,並加速您的 Web 應用程式。
Configuration
您應用程式的快取設定檔位於 config/cache.php
。在這個檔案中,您可以指定您希望應用程式預設使用的快取儲存體。Laravel 支援流行的快取後端,例如 Memcached、Redis、DynamoDB 和關聯式資料庫。此外,還提供基於檔案的快取驅動程式,而 array
和「null」快取驅動程式則為您的自動化測試提供方便的快取後端。
快取設定檔還包含各種其他選項,您可以查看。預設情況下,Laravel 設定為使用 database
快取驅動程式,它將序列化的快取物件儲存在您應用程式的資料庫中。
Driver Prerequisites
Database
當使用 database
快取驅動程式時,您將需要一個資料庫表格來包含快取資料。通常,這包含在 Laravel 的預設 0001_01_01_000001_create_cache_table.php
資料庫遷移 中;但是,如果您的應用程式不包含此遷移,您可以使用 make:cache-table
Artisan 命令來建立它
1php artisan make:cache-table2 3php artisan migrate
Memcached
使用 Memcached 驅動程式需要安裝 Memcached PECL 套件。您可以在 config/cache.php
設定檔中列出所有 Memcached 伺服器。此檔案已包含 memcached.servers
項目,以助您入門
1'memcached' => [ 2 // ... 3 4 'servers' => [ 5 [ 6 'host' => env('MEMCACHED_HOST', '127.0.0.1'), 7 'port' => env('MEMCACHED_PORT', 11211), 8 'weight' => 100, 9 ],10 ],11],
如果需要,您可以將 host
選項設定為 UNIX socket 路徑。如果您這樣做,port
選項應設定為 0
1'memcached' => [ 2 // ... 3 4 'servers' => [ 5 [ 6 'host' => '/var/run/memcached/memcached.sock', 7 'port' => 0, 8 'weight' => 100 9 ],10 ],11],
Redis
在 Laravel 中使用 Redis 快取之前,您需要透過 PECL 安裝 PhpRedis PHP 擴充功能,或透過 Composer 安裝 predis/predis
套件 (~2.0)。Laravel Sail 已包含此擴充功能。此外,官方 Laravel 應用程式平台(例如 Laravel Cloud 和 Laravel Forge)預設已安裝 PhpRedis 擴充功能。
有關設定 Redis 的更多資訊,請參閱其 Laravel 文件頁面。
DynamoDB
在使用 DynamoDB 快取驅動程式之前,您必須建立一個 DynamoDB 表格來儲存所有快取資料。通常,此表格應命名為 cache
。但是,您應該根據 cache
設定檔中 stores.dynamodb.table
設定值來命名表格。表格名稱也可以透過 DYNAMODB_CACHE_TABLE
環境變數設定。
此表格也應具有字串分割區鍵,其名稱與應用程式 cache
設定檔中 stores.dynamodb.attributes.key
設定項目的值相對應。預設情況下,分割區鍵應命名為 key
。
通常,DynamoDB 不會主動從表格中移除過期的項目。因此,您應該在表格上啟用存活時間 (TTL)。設定表格的 TTL 設定時,您應將 TTL 屬性名稱設定為 expires_at
。
接下來,安裝 AWS SDK,以便您的 Laravel 應用程式可以與 DynamoDB 通訊
1composer require aws/aws-sdk-php
此外,您應確保為 DynamoDB 快取儲存體設定選項提供值。通常,這些選項(例如 AWS_ACCESS_KEY_ID
和 AWS_SECRET_ACCESS_KEY
)應在您應用程式的 .env
設定檔中定義
1'dynamodb' => [2 'driver' => 'dynamodb',3 'key' => env('AWS_ACCESS_KEY_ID'),4 'secret' => env('AWS_SECRET_ACCESS_KEY'),5 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),6 'table' => env('DYNAMODB_CACHE_TABLE', 'cache'),7 'endpoint' => env('DYNAMODB_ENDPOINT'),8],
MongoDB
如果您正在使用 MongoDB,則官方 mongodb/laravel-mongodb
套件提供了 mongodb
快取驅動程式,並且可以使用 mongodb
資料庫連線進行設定。MongoDB 支援 TTL 索引,可用於自動清除過期的快取項目。
有關設定 MongoDB 的更多資訊,請參閱 MongoDB 快取和鎖定文件。
Cache Usage
Obtaining a Cache Instance
若要取得快取儲存體實例,您可以使用 Cache
facade,這也是我們將在本文檔中使用的。Cache
facade 提供對 Laravel 快取契約底層實作的方便、簡潔的存取
1<?php 2 3namespace App\Http\Controllers; 4 5use Illuminate\Support\Facades\Cache; 6 7class UserController extends Controller 8{ 9 /**10 * Show a list of all users of the application.11 */12 public function index(): array13 {14 $value = Cache::get('key');15 16 return [17 // ...18 ];19 }20}
存取多個快取儲存體
使用 Cache
facade,您可以透過 store
方法存取各種快取儲存體。傳遞給 store
方法的鍵應與 cache
設定檔中 stores
設定陣列中列出的儲存體之一相對應
1$value = Cache::store('file')->get('foo');2 3Cache::store('redis')->put('bar', 'baz', 600); // 10 Minutes
Retrieving Items From the Cache
Cache
facade 的 get
方法用於從快取中檢索項目。如果快取中不存在該項目,則會傳回 null
。如果您願意,您可以將第二個引數傳遞給 get
方法,指定如果該項目不存在時您希望傳回的預設值
1$value = Cache::get('key');2 3$value = Cache::get('key', 'default');
您甚至可以將閉包作為預設值傳遞。如果快取中不存在指定的項目,則會傳回閉包的結果。傳遞閉包可讓您延遲從資料庫或其他外部服務檢索預設值
1$value = Cache::get('key', function () {2 return DB::table(/* ... */)->get();3});
判斷項目是否存在
has
方法可用於判斷快取中是否存在某個項目。如果項目存在但其值為 null
,此方法也會傳回 false
1if (Cache::has('key')) {2 // ...3}
遞增/遞減值
increment
和 decrement
方法可用於調整快取中整數項目的值。這兩種方法都接受一個可選的第二個引數,指示要遞增或遞減項目值的量
1// Initialize the value if it does not exist...2Cache::add('key', 0, now()->addHours(4));3 4// Increment or decrement the value...5Cache::increment('key');6Cache::increment('key', $amount);7Cache::decrement('key');8Cache::decrement('key', $amount);
檢索和儲存
有時您可能希望從快取中檢索一個項目,但如果請求的項目不存在,也希望儲存一個預設值。例如,您可能希望從快取中檢索所有使用者,或者,如果他們不存在,則從資料庫中檢索他們並將他們新增到快取中。您可以使用 Cache::remember
方法來執行此操作
1$value = Cache::remember('users', $seconds, function () {2 return DB::table('users')->get();3});
如果快取中不存在該項目,則會執行傳遞給 remember
方法的閉包,並且其結果將放置在快取中。
您可以使用 rememberForever
方法從快取中檢索一個項目,或者如果該項目不存在,則將其永久儲存
1$value = Cache::rememberForever('users', function () {2 return DB::table('users')->get();3});
過時重新驗證
當使用 Cache::remember
方法時,如果快取值已過期,某些使用者可能會遇到回應時間緩慢的問題。對於某些類型的資料,允許在背景重新計算快取值的同時提供部分過時的資料可能很有用,從而防止某些使用者在計算快取值時遇到回應時間緩慢的問題。這通常稱為「過時重新驗證」模式,而 Cache::flexible
方法提供了此模式的實作。
flexible 方法接受一個陣列,該陣列指定快取值被視為「新鮮」的時間長度,以及何時變為「過時」。陣列中的第一個值表示快取被視為新鮮的秒數,而第二個值定義了它可以作為過時資料提供服務多長時間,之後才需要重新計算。
如果在新鮮期內(在第一個值之前)發出請求,則會立即傳回快取,而無需重新計算。如果在過時期內(在兩個值之間)發出請求,則會將過時值提供給使用者,並註冊一個 延遲函數,以便在將回應傳送給使用者後重新整理快取值。如果在第二個值之後發出請求,則快取被視為已過期,並且會立即重新計算該值,這可能會導致使用者的回應速度變慢
1$value = Cache::flexible('users', [5, 10], function () {2 return DB::table('users')->get();3});
檢索和刪除
如果您需要從快取中檢索一個項目,然後刪除該項目,您可以使用 pull
方法。與 get
方法一樣,如果快取中不存在該項目,則會傳回 null
1$value = Cache::pull('key');2 3$value = Cache::pull('key', 'default');
Storing Items in the Cache
您可以使用 Cache
facade 上的 put
方法將項目儲存在快取中
1Cache::put('key', 'value', $seconds = 10);
如果未將儲存時間傳遞給 put
方法,則該項目將無限期儲存
1Cache::put('key', 'value');
除了將秒數作為整數傳遞之外,您也可以傳遞一個 DateTime
實例,表示快取項目的所需到期時間
1Cache::put('key', 'value', now()->addMinutes(10));
如果不存在則儲存
只有當快取儲存體中尚不存在該項目時,add
方法才會將該項目新增到快取中。如果該項目實際上已新增到快取中,則該方法將傳回 true
。否則,該方法將傳回 false
。add
方法是一種原子操作
1Cache::add('key', 'value', $seconds);
永久儲存項目
forever
方法可用於永久地將項目儲存在快取中。由於這些項目不會過期,因此必須使用 forget
方法從快取中手動移除它們
1Cache::forever('key', 'value');
如果您正在使用 Memcached 驅動程式,則當快取達到其大小限制時,可能會移除「永久」儲存的項目。
Removing Items From the Cache
您可以使用 forget
方法從快取中移除項目
1Cache::forget('key');
您也可以透過提供零或負數的到期秒數來移除項目
1Cache::put('key', 'value', 0);2 3Cache::put('key', 'value', -5);
您可以使用 flush
方法清除整個快取
1Cache::flush();
清除快取不會考慮您設定的快取「prefix」,並且會從快取中移除所有條目。當清除由其他應用程式共用的快取時,請仔細考慮這一點。
The Cache Helper
除了使用 Cache
facade 之外,您也可以使用全域 cache
函數透過快取檢索和儲存資料。當使用單個字串引數呼叫 cache
函數時,它將傳回給定鍵的值
1$value = cache('key');
如果您向函數提供鍵/值對陣列和到期時間,它將在快取中儲存指定持續時間的值
1cache(['key' => 'value'], $seconds);2 3cache(['key' => 'value'], now()->addMinutes(10));
當不帶任何引數呼叫 cache
函數時,它會傳回 Illuminate\Contracts\Cache\Factory
實作的實例,允許您呼叫其他快取方法
1cache()->remember('users', $seconds, function () {2 return DB::table('users')->get();3});
當測試對全域 cache
函數的呼叫時,您可以像 測試 facade 一樣使用 Cache::shouldReceive
方法。
Atomic Locks
若要使用此功能,您的應用程式必須使用 memcached
、redis
、dynamodb
、database
、file
或 array
快取驅動程式作為應用程式的預設快取驅動程式。此外,所有伺服器都必須與同一個中央快取伺服器通訊。
Managing Locks
原子鎖允許操作分散式鎖,而無需擔心競爭條件。例如,Laravel Cloud 使用原子鎖來確保一次只在伺服器上執行一個遠端任務。您可以使用 Cache::lock
方法建立和管理鎖
1use Illuminate\Support\Facades\Cache;2 3$lock = Cache::lock('foo', 10);4 5if ($lock->get()) {6 // Lock acquired for 10 seconds...7 8 $lock->release();9}
get
方法也接受閉包。在執行閉包後,Laravel 將自動釋放鎖
1Cache::lock('foo', 10)->get(function () {2 // Lock acquired for 10 seconds and automatically released...3});
如果鎖在您請求時不可用,您可以指示 Laravel 等待指定的秒數。如果在指定的時限內無法取得鎖,則會擲回 Illuminate\Contracts\Cache\LockTimeoutException
1use Illuminate\Contracts\Cache\LockTimeoutException; 2 3$lock = Cache::lock('foo', 10); 4 5try { 6 $lock->block(5); 7 8 // Lock acquired after waiting a maximum of 5 seconds... 9} catch (LockTimeoutException $e) {10 // Unable to acquire lock...11} finally {12 $lock->release();13}
上面的範例可以透過將閉包傳遞給 block
方法來簡化。當閉包傳遞給此方法時,Laravel 將嘗試在指定的秒數內取得鎖,並在閉包執行完成後自動釋放鎖
1Cache::lock('foo', 10)->block(5, function () {2 // Lock acquired after waiting a maximum of 5 seconds...3});
Managing Locks Across Processes
有時,您可能希望在一個進程中取得鎖,並在另一個進程中釋放它。例如,您可能在 Web 請求期間取得鎖,並希望在由該請求觸發的佇列工作結束時釋放鎖。在這種情況下,您應該將鎖的範圍限定的「擁有者權杖」傳遞給佇列工作,以便該工作可以使用給定的權杖重新實例化鎖。
在下面的範例中,如果成功取得鎖,我們將分派一個佇列工作。此外,我們將透過鎖的 owner
方法將鎖的擁有者權杖傳遞給佇列工作
1$podcast = Podcast::find($id);2 3$lock = Cache::lock('processing', 120);4 5if ($lock->get()) {6 ProcessPodcast::dispatch($podcast, $lock->owner());7}
在我們應用程式的 ProcessPodcast
工作中,我們可以使用擁有者權杖還原和釋放鎖
1Cache::restoreLock('processing', $this->owner)->release();
如果您想要釋放鎖,而不考慮其目前的擁有者,您可以使用 forceRelease
方法
1Cache::lock('processing')->forceRelease();
Adding Custom Cache Drivers
Writing the Driver
若要建立我們的自訂快取驅動程式,我們首先需要實作 Illuminate\Contracts\Cache\Store
契約。因此,MongoDB 快取實作可能如下所示
1<?php 2 3namespace App\Extensions; 4 5use Illuminate\Contracts\Cache\Store; 6 7class MongoStore implements Store 8{ 9 public function get($key) {}10 public function many(array $keys) {}11 public function put($key, $value, $seconds) {}12 public function putMany(array $values, $seconds) {}13 public function increment($key, $value = 1) {}14 public function decrement($key, $value = 1) {}15 public function forever($key, $value) {}16 public function forget($key) {}17 public function flush() {}18 public function getPrefix() {}19}
我們只需要使用 MongoDB 連線實作這些方法中的每一個。有關如何實作這些方法中的每一個的範例,請查看 Laravel 框架原始碼中的 Illuminate\Cache\MemcachedStore
。完成我們的實作後,我們可以透過呼叫 Cache
facade 的 extend
方法來完成我們的自訂驅動程式註冊
1Cache::extend('mongo', function (Application $app) {2 return Cache::repository(new MongoStore);3});
如果您想知道將自訂快取驅動程式程式碼放在哪裡,您可以在 app
目錄中建立一個 Extensions
命名空間。但是,請記住,Laravel 沒有嚴格的應用程式結構,您可以根據自己的喜好組織您的應用程式。
Registering the Driver
若要向 Laravel 註冊自訂快取驅動程式,我們將使用 Cache
facade 上的 extend
方法。由於其他服務提供者可能會嘗試在其 boot
方法中讀取快取值,因此我們將在 booting
回呼中註冊我們的自訂驅動程式。透過使用 booting
回呼,我們可以確保在我們應用程式的服務提供者上呼叫 boot
方法之前,但在所有服務提供者上呼叫 register
方法之後立即註冊自訂驅動程式。我們將在應用程式 App\Providers\AppServiceProvider
類別的 register
方法中註冊我們的 booting
回呼
1<?php 2 3namespace App\Providers; 4 5use App\Extensions\MongoStore; 6use Illuminate\Contracts\Foundation\Application; 7use Illuminate\Support\Facades\Cache; 8use Illuminate\Support\ServiceProvider; 9 10class AppServiceProvider extends ServiceProvider11{12 /**13 * Register any application services.14 */15 public function register(): void16 {17 $this->app->booting(function () {18 Cache::extend('mongo', function (Application $app) {19 return Cache::repository(new MongoStore);20 });21 });22 }23 24 /**25 * Bootstrap any application services.26 */27 public function boot(): void28 {29 // ...30 }31}
傳遞給 extend
方法的第一個引數是驅動程式的名稱。這將對應於 config/cache.php
設定檔中的 driver
選項。第二個引數是一個閉包,它應該傳回 Illuminate\Cache\Repository
實例。閉包將傳遞一個 $app
實例,它是 服務容器 的實例。
註冊擴充功能後,更新 CACHE_STORE
環境變數或應用程式 config/cache.php
設定檔中的 default
選項,以使用您的擴充功能的名稱。
Events
若要在每次快取操作時執行程式碼,您可以監聽快取分派的各種 事件
事件名稱 |
---|
Illuminate\Cache\Events\CacheHit |
Illuminate\Cache\Events\CacheMissed |
Illuminate\Cache\Events\KeyForgotten |
Illuminate\Cache\Events\KeyWritten |
為了提高效能,您可以透過在應用程式的 config/cache.php
設定檔中將給定快取儲存體的 events
設定選項設定為 false
來停用快取事件
1'database' => [2 'driver' => 'database',3 // ...4 'events' => false,5],