> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# 缓存

> 学习如何使用 Laravel 缓存系统提升应用性能。

## 什么是缓存

数据库查询、外部 API 调用等操作 CPU 与网络成本高，可能耗时数秒。
如果反复获取相同数据，可以把结果保存到**缓存**，后续请求就能高速处理。

Laravel 提供了统一 API，支持 Memcached、Redis、DynamoDB、数据库等多种缓存后端。

<Info>
  默认使用 `database` 驱动。切换到 Redis 或 Memcached 可获得更高性能。
</Info>

## 缓存配置

### config/cache.php

缓存配置集中在 `config/cache.php`。通过 `CACHE_STORE` 环境变量选择默认驱动：

```php theme={null}
// config/cache.php
'default' => env('CACHE_STORE', 'database'),
```

### 可用驱动

<AccordionGroup>
  <Accordion title="database（默认）">
    将序列化后的缓存数据保存到数据库表。Laravel 11 及以上的新项目已包含迁移。

    ```ini theme={null}
    CACHE_STORE=database
    ```

    如无迁移可使用：

    ```shell theme={null}
    php artisan make:cache-table
    php artisan migrate
    ```
  </Accordion>

  <Accordion title="file">
    保存到文件系统。无需额外配置，适合小型应用。

    ```ini theme={null}
    CACHE_STORE=file
    ```
  </Accordion>

  <Accordion title="redis">
    内存中高速缓存，生产环境最常用。需要 PhpRedis 扩展或 `predis/predis`。

    ```ini theme={null}
    CACHE_STORE=redis
    REDIS_HOST=127.0.0.1
    REDIS_PORT=6379
    ```
  </Accordion>

  <Accordion title="memcached">
    需要 Memcached PECL 扩展。在 `config/cache.php` 中配置服务器：

    ```php theme={null}
    'memcached' => [
        'servers' => [
            [
                'host' => env('MEMCACHED_HOST', '127.0.0.1'),
                'port' => env('MEMCACHED_PORT', 11211),
                'weight' => 100,
            ],
        ],
    ],
    ```
  </Accordion>

  <Accordion title="dynamodb">
    使用 AWS DynamoDB。需先创建表并安装 AWS SDK：

    ```shell theme={null}
    composer require aws/aws-sdk-php
    ```

    ```ini theme={null}
    CACHE_STORE=dynamodb
    DYNAMODB_CACHE_TABLE=cache
    AWS_DEFAULT_REGION=us-east-1
    AWS_ACCESS_KEY_ID=your-key-id
    AWS_SECRET_ACCESS_KEY=your-secret-key
    ```
  </Accordion>

  <Accordion title="storage">
    将任意 Filesystem 磁盘作为键值缓存。适合直接复用已有 S3 磁盘：

    ```php theme={null}
    'storage' => [
        'driver' => 'storage',
        'disk' => env('CACHE_STORAGE_DISK'),
        'path' => env('CACHE_STORAGE_PATH', 'framework/cache/data'),
    ],
    ```
  </Accordion>

  <Accordion title="array / null（测试用）">
    `array` 只在请求内有效，`null` 忽略所有操作。用于自动化测试。

    ```ini theme={null}
    CACHE_STORE=array
    ```
  </Accordion>
</AccordionGroup>

### 缓存驱动层次

```mermaid theme={null}
flowchart LR
    A["应用"] --> B["Cache 门面"]

    subgraph L1 ["L1：请求内（最快）"]
        C["array<br>测试 / 开发"]
        D["memo（包装）<br>减少重复访问"]
    end

    subgraph L2 ["L2：内存（快）"]
        E["Redis<br>生产推荐"]
        F["Memcached<br>分布式"]
    end

    subgraph L3 ["L3：持久化（标准）"]
        G["database（默认）"]
        H["file"]
        I["DynamoDB（AWS）"]
        J["storage<br>S3 等磁盘"]
    end

    B --> C
    B --> D
    B --> E
    B --> F
    B --> G
    B --> H
    B --> I
    B --> J
```

## 基本操作

### 获取 Cache 门面

```php theme={null}
<?php

namespace App\Http\Controllers;

use Illuminate\Support\Facades\Cache;

class UserController extends Controller
{
    public function index(): array
    {
        $value = Cache::get('key');

        return [
            // ...
        ];
    }
}
```

多个存储切换：

```php theme={null}
$value = Cache::store('file')->get('foo');

Cache::store('redis')->put('bar', 'baz', 600); // 10 分钟
```

### 取值：`Cache::get()`

不存在时返回 `null`，可指定默认值。

```php theme={null}
$value = Cache::get('key');

$value = Cache::get('key', 'default');

$value = Cache::get('key', function () {
    return DB::table('settings')->get();
});
```

### 存值：`Cache::put()`

```php theme={null}
// 10 秒
Cache::put('key', 'value', 10);

// Carbon
Cache::put('key', 'value', now()->plus(minutes: 10));

// 永久
Cache::put('key', 'value');
```

只在键不存在时存入用 `add()`（原子操作）：

```php theme={null}
Cache::add('key', 'value', $seconds);
```

永久存储：

```php theme={null}
Cache::forever('key', 'value');
```

### 取或存：`Cache::remember()`

**最常用的方式**。若缓存中已有则返回，无则运行闭包并存回。

```php theme={null}
$users = Cache::remember('users', 3600, function () {
    return DB::table('users')->get();
});
```

<Tip>
  `Cache::remember()` 一行即可完成「查缓存 → 未命中则取 → 写入缓存」的三步。数据库查询与外部 API 响应缓存都适用。
</Tip>

### remember() 流程

```mermaid theme={null}
flowchart TD
    A["Cache::remember('key', $ttl, fn)"] --> B{"缓存中存在 'key'?"}
    B -->|"命中 (Hit)"| C["从缓存取"]
    B -->|"未命中 (Miss)"| D["执行闭包<br>例如 DB 查询 / API 调用"]
    D --> E["存入缓存<br>TTL = $ttl 秒"]
    E --> F["返回数据"]
    C --> F
```

永久存储版本：

```php theme={null}
$value = Cache::rememberForever('users', function () {
    return DB::table('users')->get();
});
```

若需要知道数据是否来自缓存，可用 `rememberWithWarmth()`：

```php theme={null}
[$value, $warm] = Cache::rememberWithWarmth('users', 3600, function () {
    return DB::table('users')->get();
});

if ($warm) {
    // 来自缓存
} else {
    // 刚从闭包取回
}
```

#### Stale While Revalidate

`Cache::flexible()` 指定「新鲜期」和「可容忍陈旧期」两个时长，向用户返回旧数据的同时后台刷新缓存：

```php theme={null}
$value = Cache::flexible('users', [5, 10], function () {
    return DB::table('users')->get();
});
```

### 存在性检查

```php theme={null}
if (Cache::has('key')) {
    // 已存在
}
```

### 增减

```php theme={null}
Cache::add('key', 0, now()->addHours(4));

Cache::increment('key');
Cache::increment('key', $amount);
Cache::decrement('key');
Cache::decrement('key', $amount);
```

### 取出后删除：`Cache::pull()`

```php theme={null}
$value = Cache::pull('key');
```

### 删除：`Cache::forget()`

```php theme={null}
Cache::forget('key');

Cache::flush();
```

<Warning>
  `Cache::flush()` 会忽略「前缀」设置删除全部条目。多应用共用缓存时请谨慎。
</Warning>

`Cache::flushLocks()` 可清理所有原子锁。

```php theme={null}
Cache::flushLocks();
```

### 延长 TTL：`Cache::touch()`

```php theme={null}
Cache::touch('key', 3600);

Cache::touch('key', now()->addHours(2));
```

## 缓存 memoization

`memo` 驱动会在同一请求内把缓存值缓存到内存，减少对存储的往返：

```php theme={null}
use Illuminate\Support\Facades\Cache;

$value = Cache::memo()->get('key');
$value = Cache::memo('redis')->get('key');
```

```php theme={null}
$value = Cache::memo()->get('key'); // 访问存储
$value = Cache::memo()->get('key'); // 从内存返回
```

## 缓存标签

将相关缓存分组，可整体删除。

<Warning>
  `file`、`dynamodb`、`database`、`storage` 驱动不支持缓存标签。需 `redis` 或 `memcached`。
</Warning>

### 结构

```mermaid theme={null}
flowchart TD
    A["Cache::tags(['people', 'artists'])<br>.put('John', $data)"] --> B["John 的缓存"]
    C["Cache::tags(['people', 'authors'])<br>.put('Anne', $data)"] --> D["Anne 的缓存"]

    B --> T1["Tag: people"]
    B --> T2["Tag: artists"]
    D --> T1
    D --> T3["Tag: authors"]

    T1 -->|"flush()"| E["John、Anne 都删"]
    T2 -->|"flush()"| F["只删 John"]
    T3 -->|"flush()"| G["只删 Anne"]
```

### 存取

```php theme={null}
use Illuminate\Support\Facades\Cache;

Cache::tags(['people', 'artists'])->put('John', $john, $seconds);
Cache::tags(['people', 'authors'])->put('Anne', $anne, $seconds);

$john = Cache::tags(['people', 'artists'])->get('John');
$anne = Cache::tags(['people', 'authors'])->get('Anne');
```

### 删除

```php theme={null}
Cache::tags(['people', 'authors'])->flush();

Cache::tags('authors')->flush();
```

<Tip>
  按用户或文章分组失效缓存时非常有用。例如 `Cache::tags(['user', "user:{$userId}"])->flush()` 清除该用户相关的所有缓存。
</Tip>

## 原子操作（锁）

`Cache::lock()` 可实现分布式锁，避免并发冲突。

<Info>
  该功能在 `memcached`、`redis`、`dynamodb`、`database`、`file`、`array` 驱动中可用。所有服务器需连接同一中心缓存服务器。
</Info>

### 基础锁

```php theme={null}
use Illuminate\Support\Facades\Cache;

$lock = Cache::lock('foo', 10);

if ($lock->get()) {
    // 加锁成功（10 秒有效）
    $lock->release();
}
```

传闭包会在结束时自动释放：

```php theme={null}
Cache::lock('foo', 10)->get(function () {
    // 自动释放
});
```

### 等待锁

```php theme={null}
use Illuminate\Contracts\Cache\LockTimeoutException;

$lock = Cache::lock('foo', 10);

try {
    $lock->block(5); // 最多等 5 秒
    // 处理...
} catch (LockTimeoutException $e) {
    // 获取失败
} finally {
    $lock->release();
}
```

或用闭包：

```php theme={null}
Cache::lock('foo', 10)->block(5, function () {
    // 最多等 5 秒，结束后自动释放
});
```

### 防止重复执行：`withoutOverlapping()`

```php theme={null}
Cache::withoutOverlapping('foo', function () {
    // 同时只有一个在运行
});

Cache::withoutOverlapping('foo', function () {
    // ...
}, lockFor: 120, waitFor: 5);
```

### 限制并发数：`funnel()`

```php theme={null}
Cache::funnel('foo')
    ->limit(3)
    ->releaseAfter(60)
    ->block(10)
    ->then(function () {
        // 成功获得配额
    }, function () {
        // 未获得
    });
```

### 跨进程移交锁

```php theme={null}
$lock = Cache::lock('processing', 120);

if ($lock->get()) {
    ProcessPodcast::dispatch($podcast, $lock->owner());
}

// 在任务里释放
Cache::restoreLock('processing', $this->owner)->release();
```

强制释放：

```php theme={null}
Cache::lock('processing')->forceRelease();
```

### 刷新锁

延长当前锁的 TTL：

```php theme={null}
$lock = Cache::lock('generate-reports', 60);

if ($lock->get()) {
    foreach ($reports as $report) {
        $report->generate();
        $lock->refresh();
    }

    $lock->release();
}
```

## 缓存辅助函数

`cache()` 提供更简洁的写法：

```php theme={null}
$value = cache('key');

cache(['key' => 'value'], $seconds);
cache(['key' => 'value'], now()->addMinutes(10));

cache()->remember('users', $seconds, function () {
    return DB::table('users')->get();
});
```

## 实践示例

### 缓存数据库查询

<Steps>
  <Step title="在控制器中缓存">
    ```php theme={null}
    use Illuminate\Support\Facades\Cache;
    use Illuminate\Support\Facades\DB;

    public function index(): array
    {
        $users = Cache::remember('all-users', 3600, function () {
            return DB::table('users')->orderBy('name')->get();
        });

        return compact('users');
    }
    ```
  </Step>

  <Step title="数据更新时清除">
    ```php theme={null}
    public function store(Request $request): RedirectResponse
    {
        User::create($request->validated());

        Cache::forget('all-users');

        return redirect()->route('users.index');
    }
    ```
  </Step>
</Steps>

### 缓存 Eloquent 模型

```php theme={null}
use App\Models\Product;
use Illuminate\Support\Facades\Cache;

public function byCategory(int $categoryId): array
{
    $products = Cache::remember(
        "products:category:{$categoryId}",
        3600,
        fn () => Product::where('category_id', $categoryId)
            ->where('is_active', true)
            ->orderBy('name')
            ->get()
    );

    return compact('products');
}
```

### 缓存 API 响应

```php theme={null}
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;

public function getWeather(string $city): array
{
    return Cache::remember(
        "weather:{$city}",
        1800,
        function () use ($city) {
            $response = Http::get('https://api.weather.example.com/current', [
                'city' => $city,
            ]);

            return $response->json();
        }
    );
}
```

### 使用标签分组

```php theme={null}
use Illuminate\Support\Facades\Cache;

class ArticleController extends Controller
{
    public function show(Article $article): array
    {
        $data = Cache::tags(['articles', "article:{$article->id}"])
            ->remember("article:{$article->id}:detail", 3600, function () use ($article) {
                return $article->load(['author', 'tags', 'comments']);
            });

        return compact('data');
    }

    public function update(Request $request, Article $article): RedirectResponse
    {
        $article->update($request->validated());

        Cache::tags(["article:{$article->id}"])->flush();

        return redirect()->route('articles.show', $article);
    }
}
```

## 小结

<AccordionGroup>
  <Accordion title="常用方法">
    | 方法                                 | 说明       |
    | ---------------------------------- | -------- |
    | `Cache::get('key')`                | 取        |
    | `Cache::put('key', $value, $ttl)`  | 存        |
    | `Cache::remember('key', $ttl, fn)` | 取或存（最重要） |
    | `Cache::forget('key')`             | 删        |
    | `Cache::has('key')`                | 是否存在     |
    | `Cache::flush()`                   | 清空全部     |
    | `Cache::forever('key', $value)`    | 永久       |
    | `Cache::pull('key')`               | 取后删      |
    | `Cache::increment('key')`          | +1       |
    | `Cache::tags([...])->flush()`      | 按标签清     |
  </Accordion>

  <Accordion title="驱动选型">
    * **开发 / 小型**：`file` 或 `database`
    * **生产 / 高流量**：`redis`（可搭配 Laravel Horizon 监控）
    * **AWS 环境**：`dynamodb` 或复用 S3 的 `storage`
    * **测试**：`array` 或 `null`
  </Accordion>

  <Accordion title="最佳实践">
    * 缓存键在整个应用中保持唯一（如 `users:1:profile`）
    * 数据更新后用 `Cache::forget()` 或 `Cache::tags()->flush()` 及时失效
    * TTL 要与数据更新频率匹配
    * `Cache::remember()` 让未命中的处理更简洁
    * 生产环境建议 Redis 并考虑故障转移
  </Accordion>
</AccordionGroup>


## Related topics

- [用 Laravel 构建 MCP 服务器](/zh/advanced/mcp-server.md)
- [Laravel 12 升级到 13 指南](/zh/blog/upgrade-12-to-13.md)
- [MongoDB](/zh/mongodb.md)
- [Laravel AI SDK](/zh/ai-sdk.md)
- [Eloquent 访问器、修改器与类型转换](/zh/eloquent-mutators.md)
