翻譯進度
45.78% 已翻譯
更新時間:
2024年6月30日 上午8:17:00 [世界標準時間]
翻譯人員:
幫我們翻譯此頁

快取

簡介

有些取得資料或處理任務的過程可能很消耗 CPU、或是需要數秒鐘來完成。這種時候,我們通常會將取得的資料快取住一段時間,這樣一來在接下來的請求上就能快速存取相同的資料。快取的資料通常會初存在一些非常快速的資料儲存上,如 MemcachedRedis

所幸,Laravel 為多種快取後端提供了一個表達性、統一的 API,可以享受快取提供的快速資料存取,並加速你的網站。

設定

Your application's cache configuration file is located at config/cache.php. In this file, you may specify which cache store you would like to be used by default throughout your application. Laravel supports popular caching backends like Memcached, Redis, DynamoDB, and relational databases out of the box. In addition, a file based cache driver is available, while array and "null" cache drivers provide convenient cache backends for your automated tests.

The cache configuration file also contains a variety of other options that you may review. By default, Laravel is configured to use the database cache driver, which stores the serialized, cached objects in your application's database.

Driver 需求

資料庫

When using the database cache driver, you will need a database table to contain the cache data. Typically, this is included in Laravel's default 0001_01_01_000001_create_cache_table.php database migration; however, if your application does not contain this migration, you may use the make:cache-table Artisan command to create it:

1php artisan make:cache-table
2 
3php artisan migrate
1php artisan make:cache-table
2 
3php artisan migrate

Memcached

要使用 Memcached Driver 需要安裝 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],
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 路徑。若設定為 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],
1'memcached' => [
2 // ...
3 
4 'servers' => [
5 [
6 'host' => '/var/run/memcached/memcached.sock',
7 'port' => 0,
8 'weight' => 100
9 ],
10 ],
11],

Redis

Before using a Redis cache with Laravel, you will need to either install the PhpRedis PHP extension via PECL or install the predis/predis package (~2.0) via Composer. Laravel Sail already includes this extension. In addition, official Laravel deployment platforms such as Laravel Forge and Laravel Vapor have the PhpRedis extension installed by default.

更多有關設定 Redis 的資訊,請參考 Laravel 說明文件頁面

DynamoDB

Before using the DynamoDB cache driver, you must create a DynamoDB table to store all of the cached data. Typically, this table should be named cache. However, you should name the table based on the value of the stores.dynamodb.table configuration value within the cache configuration file. The table name may also be set via the DYNAMODB_CACHE_TABLE environment variable.

該資料表也應擁有一個字串 Partition Key,其名稱應對應專案的 cache 設定檔的 stores.dynamodb.attributes.key 設定值。預設情況下,該 Partition Key 應命名為 key

使用快取

Obtaining a Cache Instance

若要取得快取儲存的實體,可以使用 Cache Facade。我們在這篇說明文件中都會使用該 Facade。Cache Facade 提供了一個方便簡潔的方式來存取 Laravel 快取 Contract 底層的實作:

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(): array
13 {
14 $value = Cache::get('key');
15 
16 return [
17 // ...
18 ];
19 }
20}
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(): array
13 {
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
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');
2 
3$value = Cache::get('key', 'default');

也可以傳入一個閉包來作為預設值。若指定項目不存在於快取內,則該閉包的結果會被回傳。傳入閉包可讓你暫緩從資料庫或其他外部服務取得預設值的過程:

1$value = Cache::get('key', function () {
2 return DB::table(/* ... */)->get();
3});
1$value = Cache::get('key', function () {
2 return DB::table(/* ... */)->get();
3});

Determining Item Existence

has 方法可以用來判斷某個項目是否存在於快取內。該方法也會在項目存在,但其值為 null 時回傳 false

1if (Cache::has('key')) {
2 // ...
3}
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);
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);

Retrieve and Store

有時候,我們可能會想要從快取內取得項目,但也想在項目不存在的時候設定預設值。舉例來說,我們可能想從快取內取得所有的使用者,但若快取不存在,則從資料庫內取得所有使用者,並存入快取。可以使用 Cache::remember 方法:

1$value = Cache::remember('users', $seconds, function () {
2 return DB::table('users')->get();
3});
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});
1$value = Cache::rememberForever('users', function () {
2 return DB::table('users')->get();
3});

Retrieve and Delete

若有需要從快取內取得並同時刪除項目,則可以使用 pull 方法。與 get 方法類似,當項目不存在於快取內時,會回傳 null

1$value = Cache::pull('key');
1$value = Cache::pull('key');

Storing Items in the Cache

可以使用 Cache Facade 上的 put 方法來將項目存入快取:

1Cache::put('key', 'value', $seconds = 10);
1Cache::put('key', 'value', $seconds = 10);

若未傳入儲存時間給 put 方法,則該項目將被永久儲存:

1Cache::put('key', 'value');
1Cache::put('key', 'value');

除了將秒數作為整數傳入,也可以傳入一個 DateTime 實體來代表指定的快取項目過期時間:

1Cache::put('key', 'value', now()->addMinutes(10));
1Cache::put('key', 'value', now()->addMinutes(10));

Store if Not Present

add 方法會只在項目不存在於快取儲存內時將項目加進快取內。該方法會在項目有真正被加進快取後回傳 true。否則,該方法會回傳 falseadd 方法是一個不可部分完成的操作(Atomic):

1Cache::add('key', 'value', $seconds);
1Cache::add('key', 'value', $seconds);

永久儲存項目

forever 方法可用來將項目永久儲存於快取。由於這些項目永遠不會過期,因此這些項目必須手動使用 forget 方法來移除:

1Cache::forever('key', 'value');
1Cache::forever('key', 'value');
lightbulb

若使用 Memcached Driver,使用「forever」儲存的項目可能會在快取達到大小限制時被移除。

Removing Items From the Cache

可以使用 forget 方法來自快取內移除項目:

1Cache::forget('key');
1Cache::forget('key');

也可以提供 0 或負數的過期時間來移除項目:

1Cache::put('key', 'value', 0);
2 
3Cache::put('key', 'value', -5);
1Cache::put('key', 'value', 0);
2 
3Cache::put('key', 'value', -5);

可以使用 flush 方法來移除整個快取:

1Cache::flush();
1Cache::flush();
lightbulb

使用 Flush 移除快取並不理會所設定的快取「Prefix(前置詞)」,會將快取內所有的項目都移除。當快取有與其他應用程式共用時,在清除快取前請三思。

Cache 輔助函式

除了使用 Cache Facade,也可以使用全域的 cache 函式來自快取內取得與儲存資料。當使用單一的字串引數呼叫 cache 方法時,會回傳給定索引鍵的值:

1$value = cache('key');
1$value = cache('key');

若傳入一組索引鍵/值配對的陣列以及一個過期時間給該函式,則會將數值初存在快取內一段給定的期間:

1cache(['key' => 'value'], $seconds);
2 
3cache(['key' => 'value'], now()->addMinutes(10));
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});
1cache()->remember('users', $seconds, function () {
2 return DB::table('users')->get();
3});
lightbulb

在測試呼叫全域的 cache 函式時,可以像在測試 Facade一樣,使用 Cache::shouldReceive 方法。

Atomic Lock (不可部分完成的鎖定)

lightbulb

若要使用此功能,則應用程式必須要使用 memcached, redis, dynamodb, database, filearray 作為應用程式的預設快取 Driver。另外,所有的伺服器也都必須要連線至相同的中央快取伺服器。

管理 Lock

使用 Atomic Lock (不可部分完成鎖定),在操作與分配 Lock 時即可不需理會競爭條件 (Race Condition)。舉例來說,Laravel Forge 使用 Atomic Lock 來確保在一台伺服器上一次只有一個遠端任務在執行。可以通過 Cache::lock 方法來建立與管理 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}
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 會自動釋放 Lock:

1Cache::lock('foo', 10)->get(function () {
2 // Lock acquired for 10 seconds and automatically released...
3});
1Cache::lock('foo', 10)->get(function () {
2 // Lock acquired for 10 seconds and automatically released...
3});

若在要求時無法取得 Lock,則可以告訴 Laravel 要等待多少秒的事件。若在指定的時間限制後仍無法取得 Lock,則會擲回 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}
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 會嘗試在指定秒數內取得 Lock,並在閉包執行後自動釋放 Lock:

1Cache::lock('foo', 10)->block(5, function () {
2 // Lock acquired after waiting a maximum of 5 seconds...
3});
1Cache::lock('foo', 10)->block(5, function () {
2 // Lock acquired after waiting a maximum of 5 seconds...
3});

在多個處理程序間管理 Lock

有的時候我們可能想要在一個處理程序內要求 Lock,並在另一個處理程序中釋放。舉例來說,我們可能會在某個網頁請求的期間內要求 Lock,並在由該請求觸發的佇列任務完成後才釋放該 Lock。在此情境中,應將該 Lock 的區域性「擁有者權杖」傳給佇列任務,以讓佇列任務可以使用給定的權杖來重新取得 Lock。

在下方的範例中,我們會在成功取得 Lock 後分派佇列任務。另外,我們也會通過 Lock 的 owner 方法來將 Lock 的擁有者權杖傳給佇列任務。

1$podcast = Podcast::find($id);
2 
3$lock = Cache::lock('processing', 120);
4 
5if ($lock->get()) {
6 ProcessPodcast::dispatch($podcast, $lock->owner());
7}
1$podcast = Podcast::find($id);
2 
3$lock = Cache::lock('processing', 120);
4 
5if ($lock->get()) {
6 ProcessPodcast::dispatch($podcast, $lock->owner());
7}

在專案的 ProcessPodcast 任務中,我們可以通過擁有者權杖來恢復與釋放 Lock:

1Cache::restoreLock('processing', $this->owner)->release();
1Cache::restoreLock('processing', $this->owner)->release();

若想在不理會目前擁有者的情況下釋放 Lock,可以使用 forceRelease 方法:

1Cache::lock('processing')->forceRelease();
1Cache::lock('processing')->forceRelease();

新增自訂快取 Driver

Writing the Driver

若要建立自訂快取 Driver,首先必須實作 Illuminate\Contracts\Cache\Store Contract。因此,一個 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}
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 方法來註冊自訂 Driver:

1Cache::extend('mongo', function (Application $app) {
2 return Cache::repository(new MongoStore);
3});
1Cache::extend('mongo', function (Application $app) {
2 return Cache::repository(new MongoStore);
3});
lightbulb

若不知道該將自定快取 Driver 的程式碼放在哪裡,可在 app 目錄內建立一個 Extensions 命名空間。不過,請記得,Laravel 並沒有硬性規定應用程式的架構,你可以隨意依照你的喜好來阻止程式碼。

Registering the Driver

若要向 Laravel 註冊自訂快取 Driver,可以使用 Cache Facade 上的 extend 方法。由於其他的 Service Provider 可能會嘗試在 boot 方法內讀取快取值,因此我們需要將自訂 Driver 註冊在 booting 回呼內。只要使用了 booting 回呼,就能確保自訂回呼是在其他 Service Provider 的 boot 方法被呼叫前、以及 App\Providers\AppServiceProvider 類別的 register 方法被呼叫前被註冊的。我們會將 booting 回呼放在專案的 App\Providers\AppServiceProvider 類別中的 register 方法內:

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 ServiceProvider
11{
12 /**
13 * Register any application services.
14 */
15 public function register(): void
16 {
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(): void
28 {
29 // ...
30 }
31}
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 ServiceProvider
11{
12 /**
13 * Register any application services.
14 */
15 public function register(): void
16 {
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(): void
28 {
29 // ...
30 }
31}

傳入 extend 方法的第一個引數為 Driver 的名稱。這個名稱應對應到 config/cache.php 設定檔中的 driver 選項。第二個引數則是一個應回傳 Illuminate\Cache\Repository 實體的閉包。該閉包會被傳入一個 $app 實體,即為 Service Container 的實體。

Once your extension is registered, update the CACHE_STORE environment variable or default option within your application's config/cache.php configuration file to the name of your extension.

事件

To execute code on every cache operation, you may listen for various events dispatched by the cache:

Event Name
Illuminate\Cache\Events\CacheHit
Illuminate\Cache\Events\CacheMissed
Illuminate\Cache\Events\KeyForgotten
Illuminate\Cache\Events\KeyWritten
翻譯進度
45.78% 已翻譯
更新時間:
2024年6月30日 上午8:17:00 [世界標準時間]
翻譯人員:
幫我們翻譯此頁

留言

尚無留言

“Laravel” is a Trademark of Taylor Otwell.
The source documentation is released under MIT license. See laravel/docs on GitHub for details.
The translated documentations are released under MIT license. See cornch/laravel-docs-l10n on GitHub for details.