Laravel 13 缓存高级特性深度解析

缓存是提高应用程序性能的关键技术。本文将深入探讨 Laravel 13 中缓存的高级特性。

缓存基础回顾

基本操作

1
2
3
4
5
6
7
8
9
<?php

use Illuminate\Support\Facades\Cache;

Cache::put('key', 'value', $seconds);
Cache::get('key', 'default');
Cache::has('key');
Cache::forget('key');
Cache::flush();

原子锁

基本锁操作

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php

use Illuminate\Support\Facades\Cache;

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

if ($lock->get()) {
try {
$this->process();
} finally {
$lock->release();
}
}

锁等待

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php

use Illuminate\Support\Facades\Cache;

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

if ($lock->block(5)) {
try {
$this->process();
} finally {
$lock->release();
}
}

自动释放

1
2
3
4
5
6
7
8
9
10
11
<?php

use Illuminate\Support\Facades\Cache;

Cache::lock('processing', 10)->get(function () {
$this->process();
});

Cache::lock('processing', 10)->block(5, function () {
$this->process();
});

跨进程锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php

use Illuminate\Support\Facades\Cache;

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

if ($lock->get()) {
$lockOwner = $lock->owner();

Cache::put('lock:owner', $lockOwner, 10);
}

$storedOwner = Cache::get('lock:owner');

if ($lock->isOwnedBy($storedOwner)) {
$lock->release();
}

缓存标签

标签操作

1
2
3
4
5
6
7
8
9
10
11
12
<?php

use Illuminate\Support\Facades\Cache;

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

$john = Cache::tags(['people', 'artists'])->get('john');

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

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

标签缓存库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php

namespace App\Services;

use Illuminate\Support\Facades\Cache;

class ArticleCacheService
{
protected string $tag = 'articles';

public function remember(int $id, callable $callback)
{
return Cache::tags([$this->tag])->remember(
"article.{$id}",
3600,
$callback
);
}

public function forget(int $id): bool
{
return Cache::tags([$this->tag])->forget("article.{$id}");
}

public function flush(): void
{
Cache::tags([$this->tag])->flush();
}
}

缓存助手函数

remember() 方法

1
2
3
4
5
6
7
8
9
10
11
<?php

use Illuminate\Support\Facades\Cache;

$value = Cache::remember('users.active', 3600, function () {
return User::where('active', true)->get();
});

$value = Cache::rememberForever('users.active', function () {
return User::where('active', true)->get();
});

rememberForever() 方法

1
2
3
4
5
6
7
8
9
10
11
<?php

use Illuminate\Support\Facades\Cache;

$config = Cache::rememberForever('app.config', function () {
return [
'site_name' => config('app.name'),
'version' => config('app.version'),
'features' => config('features'),
];
});

增减操作

增量操作

1
2
3
4
5
6
7
8
9
<?php

use Illuminate\Support\Facades\Cache;

Cache::put('counter', 0);
Cache::increment('counter');
Cache::increment('counter', 5);
Cache::decrement('counter');
Cache::decrement('counter', 3);

原子计数器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<?php

namespace App\Services;

use Illuminate\Support\Facades\Cache;

class RateLimiter
{
protected string $key;
protected int $maxAttempts;
protected int $decayMinutes;

public function __construct(string $key, int $maxAttempts = 60, int $decayMinutes = 1)
{
$this->key = $key;
$this->maxAttempts = $maxAttempts;
$this->decayMinutes = $decayMinutes;
}

public function hit(): int
{
$key = "rate_limit:{$this->key}";

$added = Cache::add($key, 0, $this->decayMinutes * 60);

$hits = Cache::increment($key);

return $hits;
}

public function tooManyAttempts(): bool
{
$key = "rate_limit:{$this->key}";
$hits = Cache::get($key, 0);

return $hits >= $this->maxAttempts;
}

public function remaining(): int
{
$key = "rate_limit:{$this->key}";
$hits = Cache::get($key, 0);

return max(0, $this->maxAttempts - $hits);
}

public function reset(): void
{
Cache::forget("rate_limit:{$this->key}");
}
}

缓存存储

多存储支持

1
2
3
4
5
6
7
<?php

use Illuminate\Support\Facades\Cache;

$value = Cache::store('redis')->get('key');
Cache::store('file')->put('key', 'value', 3600);
Cache::store('database')->forever('key', 'value');

自定义存储

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
<?php

namespace App\Cache;

use Illuminate\Contracts\Cache\Store;

class CustomCacheStore implements Store
{
protected string $prefix;

public function __construct(string $prefix = '')
{
$this->prefix = $prefix;
}

public function get($key): mixed
{
return $this->retrieve($this->prefix . $key);
}

public function many(array $keys): array
{
$values = [];

foreach ($keys as $key) {
$values[$key] = $this->get($key);
}

return $values;
}

public function put($key, $value, $seconds): bool
{
return $this->store($this->prefix . $key, $value, $seconds);
}

public function putMany(array $values, $seconds): bool
{
foreach ($values as $key => $value) {
$this->put($key, $value, $seconds);
}

return true;
}

public function increment($key, $value = 1): int|bool
{
return $this->incrementValue($this->prefix . $key, $value);
}

public function decrement($key, $value = 1): int|bool
{
return $this->decrementValue($this->prefix . $key, $value);
}

public function forever($key, $value): bool
{
return $this->store($this->prefix . $key, $value, 0);
}

public function forget($key): bool
{
return $this->delete($this->prefix . $key);
}

public function flush(): bool
{
return $this->clearAll();
}

public function getPrefix(): string
{
return $this->prefix;
}

protected function retrieve(string $key): mixed
{
return null;
}

protected function store(string $key, mixed $value, int $seconds): bool
{
return true;
}

protected function delete(string $key): bool
{
return true;
}

protected function clearAll(): bool
{
return true;
}

protected function incrementValue(string $key, int $value): int|bool
{
return 0;
}

protected function decrementValue(string $key, int $value): int|bool
{
return 0;
}
}

缓存事件

监听缓存事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Event;
use Illuminate\Cache\Events\CacheHit;
use Illuminate\Cache\Events\CacheMissed;
use Illuminate\Cache\Events\KeyForgotten;
use Illuminate\Cache\Events\KeyWritten;

class CacheEventServiceProvider extends ServiceProvider
{
public function boot(): void
{
Event::listen(CacheHit::class, function ($event) {
\Log::debug("Cache hit: {$event->key}");
});

Event::listen(CacheMissed::class, function ($event) {
\Log::debug("Cache missed: {$event->key}");
});

Event::listen(KeyWritten::class, function ($event) {
\Log::debug("Cache written: {$event->key}");
});

Event::listen(KeyForgotten::class, function ($event) {
\Log::debug("Cache forgotten: {$event->key}");
});
}
}

缓存装饰器

模型缓存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<?php

namespace App\Traits;

use Illuminate\Support\Facades\Cache;

trait Cacheable
{
protected function getCacheKey(string $key): string
{
return sprintf(
'%s:%s:%s',
$this->getTable(),
$this->getKey(),
$key
);
}

protected function cacheRemember(string $key, int $ttl, callable $callback): mixed
{
return Cache::remember($this->getCacheKey($key), $ttl, $callback);
}

protected function cacheForget(string $key): bool
{
return Cache::forget($this->getCacheKey($key));
}

protected function cacheFlush(): void
{
$pattern = $this->getTable() . ':' . $this->getKey() . ':*';

$keys = Cache::getStore()->getRedis()->keys($pattern);

foreach ($keys as $key) {
Cache::forget($key);
}
}
}

查询缓存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?php

namespace App\Services;

use Illuminate\Support\Facades\Cache;
use Illuminate\Database\Eloquent\Model;

class QueryCacheService
{
protected int $ttl = 3600;
protected string $prefix = 'query:';

public function remember(Model $model, string $method, array $args, callable $callback): mixed
{
$key = $this->generateKey($model, $method, $args);

return Cache::remember($key, $this->ttl, $callback);
}

public function forget(Model $model, string $method, array $args = []): bool
{
$key = $this->generateKey($model, $method, $args);

return Cache::forget($key);
}

public function forgetModel(Model $model): void
{
$pattern = $this->prefix . get_class($model) . ':' . $model->getKey() . ':*';

$this->flushPattern($pattern);
}

protected function generateKey(Model $model, string $method, array $args): string
{
return $this->prefix . get_class($model) . ':' . $model->getKey() . ':' . $method . ':' . md5(json_encode($args));
}

protected function flushPattern(string $pattern): void
{
$redis = Cache::getStore()->getRedis();
$keys = $redis->keys($pattern);

foreach ($keys as $key) {
Cache::forget($key);
}
}
}

缓存预热

预热命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\Cache;
use App\Models\{User, Product, Category};

class WarmCache extends Command
{
protected $signature = 'cache:warm';

protected $description = 'Warm up application cache';

public function handle(): int
{
$this->info('Warming cache...');

$this->warmUsers();
$this->warmProducts();
$this->warmCategories();

$this->info('Cache warmed successfully!');

return 0;
}

protected function warmUsers(): void
{
$this->info('Warming users cache...');

Cache::remember('users.active', 3600, function () {
return User::where('active', true)->get();
});

Cache::remember('users.count', 3600, function () {
return User::count();
});
}

protected function warmProducts(): void
{
$this->info('Warming products cache...');

Cache::remember('products.featured', 3600, function () {
return Product::where('featured', true)->get();
});

Cache::remember('products.categories', 3600, function () {
return Category::withCount('products')->get();
});
}

protected function warmCategories(): void
{
$this->info('Warming categories cache...');

Cache::remember('categories.all', 3600, function () {
return Category::with('children')->whereNull('parent_id')->get();
});
}
}

缓存失效策略

基于时间的失效

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?php

namespace App\Services;

use Illuminate\Support\Facades\Cache;

class ConfigurationService
{
protected int $ttl = 3600;

public function get(string $key, mixed $default = null): mixed
{
return Cache::remember("config.{$key}", $this->ttl, function () use ($key, $default) {
return \App\Models\Configuration::where('key', $key)->value('value') ?? $default;
});
}

public function set(string $key, mixed $value): void
{
\App\Models\Configuration::updateOrCreate(
['key' => $key],
['value' => $value]
);

Cache::forget("config.{$key}");
}

public function flush(): void
{
Cache::flush();
}
}

基于事件的失效

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php

namespace App\Observers;

use App\Models\Product;
use Illuminate\Support\Facades\Cache;

class ProductObserver
{
public function created(Product $product): void
{
$this->clearProductCache($product);
}

public function updated(Product $product): void
{
$this->clearProductCache($product);
}

public function deleted(Product $product): void
{
$this->clearProductCache($product);
}

protected function clearProductCache(Product $product): void
{
Cache::forget("product.{$product->id}");
Cache::forget('products.featured');
Cache::forget('products.latest');

Cache::tags(['products'])->flush();
}
}

测试缓存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php

namespace Tests\Unit\Services;

use Tests\TestCase;
use App\Services\ConfigurationService;
use Illuminate\Support\Facades\Cache;

class CacheTest extends TestCase
{
public function test_cache_remember(): void
{
Cache::shouldReceive('remember')
->once()
->with('test.key', 3600, \Closure::class)
->andReturn('cached_value');

$service = new ConfigurationService();
$value = $service->get('test.key');

$this->assertEquals('cached_value', $value);
}

public function test_cache_forget(): void
{
Cache::shouldReceive('forget')
->once()
->with('config.test_key');

$service = new ConfigurationService();
$service->set('test_key', 'new_value');
}
}

最佳实践

1. 合理设置 TTL

1
2
3
4
5
<?php

Cache::put('config', $config, 86400);
Cache::put('user.session', $session, 3600);
Cache::put('rate_limit', $count, 60);

2. 使用标签组织

1
2
3
4
<?php

Cache::tags(['user:' . $userId])->put('profile', $profile, 3600);
Cache::tags(['user:' . $userId])->flush();

3. 缓存穿透保护

1
2
3
4
5
6
7
<?php

$value = Cache::remember('key', 3600, function () {
$value = Model::find($id);

return $value ?? false;
});

总结

Laravel 13 的缓存系统提供了丰富的功能来优化应用程序性能。通过合理使用原子锁、标签、事件等高级特性,可以构建高效、可靠的缓存策略。