跳到内容

缓存组件

编辑此页

Cache 组件提供了涵盖简单到高级缓存需求的功能。它原生实现了 PSR-6Cache Contracts,以实现最大的互操作性。它专为性能和弹性而设计,附带即用型适配器,适用于最常见的缓存后端。它通过锁定和提前过期实现了基于标签的失效和缓存惊群效应保护。

提示

该组件还包含用于在 PSR-6 和 PSR-16 之间进行转换的适配器。请参阅 PSR-6 和 PSR-16 缓存之间互操作性的适配器

安装

1
$ composer require symfony/cache

注意

如果您在 Symfony 应用程序之外安装此组件,则必须在代码中引入 vendor/autoload.php 文件,以启用 Composer 提供的类自动加载机制。阅读 这篇文章 以了解更多详情。

缓存契约与 PSR-6

此组件包含两种不同的缓存方法

PSR-6 缓存:
一个通用的缓存系统,涉及缓存“池”和缓存“项”。
缓存契约:
一种更简单但更强大的方法,可以根据重新计算回调来缓存值。

提示

建议使用 Cache Contracts 方法:它需要的样板代码更少,并默认提供缓存惊群效应保护。

缓存契约

所有适配器都支持 Cache Contracts。它们仅包含两个方法:get()delete()set() 方法不存在,因为 get() 方法同时获取和设置缓存值。

您需要做的第一件事是实例化一个缓存适配器。FilesystemAdapter 在此示例中使用

1
2
3
use Symfony\Component\Cache\Adapter\FilesystemAdapter;

$cache = new FilesystemAdapter();

现在您可以使用此对象检索和删除缓存数据。get() 方法的第一个参数是一个键,一个任意字符串,您将其与缓存值关联,以便稍后检索它。第二个参数是一个 PHP 可调用对象,当在缓存中找不到键时执行该对象以生成并返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
use Symfony\Contracts\Cache\ItemInterface;

// The callable will only be executed on a cache miss.
$value = $cache->get('my_cache_key', function (ItemInterface $item): string {
    $item->expiresAfter(3600);

    // ... do some HTTP request or heavy computations
    $computedValue = 'foobar';

    return $computedValue;
});

echo $value; // 'foobar'

// ... and to remove the cache key
$cache->delete('my_cache_key');

注意

使用缓存标签一次删除多个键。请阅读 缓存失效 了解更多信息。

惊群效应预防

Cache Contracts 还内置了 惊群效应预防。这将消除缓存冷启动时的 CPU 峰值。如果一个示例应用程序花费 5 秒来计算缓存 1 小时的数据,并且每秒访问此数据 10 次,这意味着您主要遇到缓存命中,一切正常。但是 1 小时后,我们收到了 10 个针对冷缓存的新请求。因此,数据再次被计算。下一秒,同样的事情发生了。因此,在缓存再次变热之前,数据被计算了大约 50 次。这就是您需要惊群效应预防的地方。

第一个解决方案是使用锁定:仅允许一个 PHP 进程(在每个主机的基础上)一次计算特定的键。锁定是默认内置的,因此您无需执行任何操作,只需利用 Cache Contracts 即可。

第二个解决方案也是在使用 Cache Contracts 时内置的:与其等待完整延迟后才使值过期,不如在其到期日期之前重新计算它。概率性提前过期 算法随机地为一个用户伪造缓存未命中,而其他用户仍然获得缓存值。您可以使用 get() 的第三个可选参数(一个名为“beta”的浮点值)来控制其行为。

默认情况下,beta 为 1.0,更高的值意味着更早的重新计算。将其设置为 0 以禁用提前重新计算,并将其设置为 INF 以强制立即重新计算

1
2
3
4
5
6
7
8
9
use Symfony\Contracts\Cache\ItemInterface;

$beta = 1.0;
$value = $cache->get('my_cache_key', function (ItemInterface $item): string {
    $item->expiresAfter(3600);
    $item->tag(['tag_0', 'tag_1']);

    return '...';
}, $beta);

通用缓存 (PSR-6)

要使用通用的 PSR-6 缓存功能,您需要学习其关键概念

存储为键/值对的单个信息单元,其中键是信息的唯一标识符,值是其内容;请参阅 缓存项 文章了解更多详情。
缓存项的逻辑存储库。所有缓存操作(保存项、查找项等)都通过池执行。应用程序可以根据需要定义任意数量的池。
适配器
它实现了实际的缓存机制,用于将信息存储在文件系统、数据库等中。该组件为常见的缓存后端(Redis、APCu、PDO 等)提供了多个即用型适配器。

基本用法 (PSR-6)

组件的这部分是 PSR-6 的实现,这意味着其基本 API 与文档中定义的相同。在开始缓存信息之前,请使用任何内置适配器创建缓存池。例如,要创建基于文件系统的缓存,请实例化 FilesystemAdapter

1
2
3
use Symfony\Component\Cache\Adapter\FilesystemAdapter;

$cache = new FilesystemAdapter();

现在您可以使用此缓存池创建、检索、更新和删除项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// create a new item by trying to get it from the cache
$productsCount = $cache->getItem('stats.products_count');

// assign a value to the item and save it
$productsCount->set(4711);
$cache->save($productsCount);

// retrieve the cache item
$productsCount = $cache->getItem('stats.products_count');
if (!$productsCount->isHit()) {
    // ... item does not exist in the cache
}
// retrieve the value stored by the item
$total = $productsCount->get();

// remove the cache item
$cache->deleteItem('stats.products_count');

有关所有受支持适配器的列表,请参阅 缓存池和支持的适配器

编组 (序列化) 数据

注意

编组序列化 是类似的概念。序列化是将对象状态转换为可以存储的格式(例如,在文件中)的过程。编组是将对象状态及其代码库转换为可以存储或传输的格式的过程。

解组对象会生成原始对象的副本,可能通过自动加载对象的类定义。

Symfony 使用编组器(实现 MarshallerInterface 的类)在存储缓存项之前对其进行处理。

DefaultMarshaller 默认使用 PHP 的 serialize() 函数,但您可以选择使用来自 Igbinary 扩展igbinary_serialize() 函数

1
2
3
4
5
6
7
8
9
use Symfony\Component\Cache\Adapter\RedisAdapter;
use Symfony\Component\Cache\Marshaller\DefaultMarshaller;
use Symfony\Component\Cache\Marshaller\DeflateMarshaller;

$marshaller = new DeflateMarshaller(new DefaultMarshaller());
// you can optionally use the Igbinary extension if you have it installed
// $marshaller = new DeflateMarshaller(new DefaultMarshaller(useIgbinarySerialize: true));

$cache = new RedisAdapter(new \Redis(), 'namespace', 0, $marshaller);

还有其他编组器可以在存储数据之前对其进行加密或压缩。

7.2

在 Symfony 7.2 之前的版本中,当安装 Igbinary 扩展时,默认使用 igbinary_serialize() 函数。从 Symfony 7.2 开始,您必须显式启用 Igbinary 支持。

本作品,包括代码示例,根据 Creative Commons BY-SA 3.0 许可协议获得许可。
目录
    版本