跳转到主要内容

什么是缓存

数据库查询、外部 API 调用等操作 CPU 与网络成本高,可能耗时数秒。 如果反复获取相同数据,可以把结果保存到缓存,后续请求就能高速处理。 Laravel 提供了统一 API,支持 Memcached、Redis、DynamoDB、数据库等多种缓存后端。
默认使用 database 驱动。切换到 Redis 或 Memcached 可获得更高性能。

缓存配置

config/cache.php

缓存配置集中在 config/cache.php。通过 CACHE_STORE 环境变量选择默认驱动:
// config/cache.php
'default' => env('CACHE_STORE', 'database'),

可用驱动

将序列化后的缓存数据保存到数据库表。Laravel 11 及以上的新项目已包含迁移。
CACHE_STORE=database
如无迁移可使用:
php artisan make:cache-table
php artisan migrate
保存到文件系统。无需额外配置,适合小型应用。
CACHE_STORE=file
内存中高速缓存,生产环境最常用。需要 PhpRedis 扩展或 predis/predis
CACHE_STORE=redis
REDIS_HOST=127.0.0.1
REDIS_PORT=6379
需要 Memcached PECL 扩展。在 config/cache.php 中配置服务器:
'memcached' => [
    'servers' => [
        [
            'host' => env('MEMCACHED_HOST', '127.0.0.1'),
            'port' => env('MEMCACHED_PORT', 11211),
            'weight' => 100,
        ],
    ],
],
使用 AWS DynamoDB。需先创建表并安装 AWS SDK:
composer require aws/aws-sdk-php
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
将任意 Filesystem 磁盘作为键值缓存。适合直接复用已有 S3 磁盘:
'storage' => [
    'driver' => 'storage',
    'disk' => env('CACHE_STORAGE_DISK'),
    'path' => env('CACHE_STORAGE_PATH', 'framework/cache/data'),
],
array 只在请求内有效,null 忽略所有操作。用于自动化测试。
CACHE_STORE=array

缓存驱动层次

基本操作

获取 Cache 门面

<?php

namespace App\Http\Controllers;

use Illuminate\Support\Facades\Cache;

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

        return [
            // ...
        ];
    }
}
多个存储切换:
$value = Cache::store('file')->get('foo');

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

取值:Cache::get()

不存在时返回 null,可指定默认值。
$value = Cache::get('key');

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

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

存值:Cache::put()

// 10 秒
Cache::put('key', 'value', 10);

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

// 永久
Cache::put('key', 'value');
只在键不存在时存入用 add()(原子操作):
Cache::add('key', 'value', $seconds);
永久存储:
Cache::forever('key', 'value');

取或存:Cache::remember()

最常用的方式。若缓存中已有则返回,无则运行闭包并存回。
$users = Cache::remember('users', 3600, function () {
    return DB::table('users')->get();
});
Cache::remember() 一行即可完成「查缓存 → 未命中则取 → 写入缓存」的三步。数据库查询与外部 API 响应缓存都适用。

remember() 流程

永久存储版本:
$value = Cache::rememberForever('users', function () {
    return DB::table('users')->get();
});
若需要知道数据是否来自缓存,可用 rememberWithWarmth()
[$value, $warm] = Cache::rememberWithWarmth('users', 3600, function () {
    return DB::table('users')->get();
});

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

Stale While Revalidate

Cache::flexible() 指定「新鲜期」和「可容忍陈旧期」两个时长,向用户返回旧数据的同时后台刷新缓存:
$value = Cache::flexible('users', [5, 10], function () {
    return DB::table('users')->get();
});

存在性检查

if (Cache::has('key')) {
    // 已存在
}

增减

Cache::add('key', 0, now()->addHours(4));

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

取出后删除:Cache::pull()

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

删除:Cache::forget()

Cache::forget('key');

Cache::flush();
Cache::flush() 会忽略「前缀」设置删除全部条目。多应用共用缓存时请谨慎。
Cache::flushLocks() 可清理所有原子锁。
Cache::flushLocks();

延长 TTL:Cache::touch()

Cache::touch('key', 3600);

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

缓存 memoization

memo 驱动会在同一请求内把缓存值缓存到内存,减少对存储的往返:
use Illuminate\Support\Facades\Cache;

$value = Cache::memo()->get('key');
$value = Cache::memo('redis')->get('key');
$value = Cache::memo()->get('key'); // 访问存储
$value = Cache::memo()->get('key'); // 从内存返回

缓存标签

将相关缓存分组,可整体删除。
filedynamodbdatabasestorage 驱动不支持缓存标签。需 redismemcached

结构

存取

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');

删除

Cache::tags(['people', 'authors'])->flush();

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

原子操作(锁)

Cache::lock() 可实现分布式锁,避免并发冲突。
该功能在 memcachedredisdynamodbdatabasefilearray 驱动中可用。所有服务器需连接同一中心缓存服务器。

基础锁

use Illuminate\Support\Facades\Cache;

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

if ($lock->get()) {
    // 加锁成功(10 秒有效)
    $lock->release();
}
传闭包会在结束时自动释放:
Cache::lock('foo', 10)->get(function () {
    // 自动释放
});

等待锁

use Illuminate\Contracts\Cache\LockTimeoutException;

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

try {
    $lock->block(5); // 最多等 5 秒
    // 处理...
} catch (LockTimeoutException $e) {
    // 获取失败
} finally {
    $lock->release();
}
或用闭包:
Cache::lock('foo', 10)->block(5, function () {
    // 最多等 5 秒,结束后自动释放
});

防止重复执行:withoutOverlapping()

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

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

限制并发数:funnel()

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

跨进程移交锁

$lock = Cache::lock('processing', 120);

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

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

刷新锁

延长当前锁的 TTL:
$lock = Cache::lock('generate-reports', 60);

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

    $lock->release();
}

缓存辅助函数

cache() 提供更简洁的写法:
$value = cache('key');

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

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

实践示例

缓存数据库查询

1

在控制器中缓存

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');
}
2

数据更新时清除

public function store(Request $request): RedirectResponse
{
    User::create($request->validated());

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

    return redirect()->route('users.index');
}

缓存 Eloquent 模型

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 响应

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();
        }
    );
}

使用标签分组

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);
    }
}

小结

方法说明
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()按标签清
  • 开发 / 小型filedatabase
  • 生产 / 高流量redis(可搭配 Laravel Horizon 监控)
  • AWS 环境dynamodb 或复用 S3 的 storage
  • 测试arraynull
  • 缓存键在整个应用中保持唯一(如 users:1:profile
  • 数据更新后用 Cache::forget()Cache::tags()->flush() 及时失效
  • TTL 要与数据更新频率匹配
  • Cache::remember() 让未命中的处理更简洁
  • 生产环境建议 Redis 并考虑故障转移
最后修改于 2026年7月13日