缓存组件
Cache 组件提供了涵盖简单到高级缓存需求的功能。它原生实现了 PSR-6 和 Cache Contracts,以实现最大的互操作性。它专为性能和弹性而设计,附带即用型适配器,适用于最常见的缓存后端。它通过锁定和提前过期实现了基于标签的失效和缓存惊群效应保护。
提示
该组件还包含用于在 PSR-6 和 PSR-16 之间进行转换的适配器。请参阅 PSR-6 和 PSR-16 缓存之间互操作性的适配器。
安装
1
$ composer require symfony/cache
注意
如果您在 Symfony 应用程序之外安装此组件,则必须在代码中引入 vendor/autoload.php
文件,以启用 Composer 提供的类自动加载机制。阅读 这篇文章 以了解更多详情。
缓存契约
所有适配器都支持 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 支持。