sura/cache

Sura缓存。

2.1.4 2022-01-15 19:46 UTC

This package is auto-updated.

Last update: 2024-09-16 01:26:20 UTC


README

Latest Stable Version License

简介

缓存通过存储数据(这些数据通常难以检索)以供将来使用,从而加速您的应用程序。

通过Composer安装Sura Cache

composer require sura/cache

它需要PHP版本8.0。

基本用法

与缓存工作的核心是Sura\Cache\Cache对象。我们创建其实例,并将所谓的存储作为参数传递给构造函数。这是一个表示数据将物理存储位置的(数据库、Memcached、磁盘上的文件等)对象。您可以在存储部分中找到所有必要的详细信息。

对于以下示例,假设我们有一个别名Cache和一个存储在变量$storage中。

use Sura\Cache\Cache;

$storage // instance of Sura\Cache\IStorage

缓存实际上是一个键-值存储,因此我们可以在键下读写数据,就像关联数组一样。应用程序由多个独立的部分组成,如果它们都使用一个存储(例如:磁盘上的一个目录),那么迟早会出现键冲突。Sura框架通过将整个空间划分为命名空间(子目录)来解决这个问题。然后程序的每个部分都使用其自己的空间和唯一的名称,从而不会发生冲突。

命名空间是作为Cache类构造函数的第二个参数指定的

$cache = new Cache($storage, 'Full Html Pages');

现在我们可以使用对象$cache从缓存中读取和写入。方法load()用于两者。第一个参数是键,第二个是PHP回调,当键在缓存中找不到时调用。回调生成值,返回它并将其缓存

$value = $cache->load($key, function () use ($key) {
	$computedValue = ...; // heavy computations
	return $computedValue;
});

如果第二个参数未指定$value = $cache->load($key),如果项目不在缓存中,则返回null

好处是任何可序列化的结构都可以缓存,而不仅仅是字符串。对于键也是如此。

使用方法remove()从缓存中清除项目

$cache->remove($key);

您也可以使用方法$cache->save($key, $value, array $dependencies = [])缓存项目。然而,使用load()的方法更受欢迎。

记忆化

记忆化意味着缓存函数或方法的结果,以便下次可以直接使用它,而不是再次计算相同的内容。

可以使用call(callable $callback, ...$args)调用记忆化的方法和函数

$result = $cache->call('gethostbyaddr', $ip);

对于每个参数$ip,函数gethostbyaddr()仅调用一次,下一次将返回缓存中的值。

也可以为方法或函数创建一个记忆化的包装器,稍后可以调用它

function factorial($num)
{
	return ...;
}

$memoizedFactorial = $cache->wrap('factorial');

$result = $memoizedFactorial(5); // counts it
$result = $memoizedFactorial(5); // returns it from cache

过期 & 无效化

在缓存中,有必要解决这样一个问题:之前保存的一些数据将随着时间的推移而变得无效。Sura框架提供了一种机制,如何限制数据的有效性以及如何以受控的方式(使用框架的术语,即“无效化”)删除它们。

数据的有效性是在保存时设置的,使用方法save()的第三个参数,例如

$cache->save($key, $value, [
	Cache::EXPIRE => '20 minutes',
]);

或者使用通过引用传递给load()方法中的回调的$dependencies参数,例如

$value = $cache->load($key, function (&$dependencies) {
	$dependencies[Cache::EXPIRE] = '20 minutes';
	return ...;
]);

在以下示例中,我们将假设第二种变体,因此存在变量$dependencies

过期

最简单的过期是时间限制。以下是缓存20分钟有效数据的方法

// it also accepts the number of seconds or the UNIX timestamp
$dependencies[Cache::EXPIRE] = '20 minutes';

如果我们想通过每次读取来延长有效期,可以通过这种方式实现,但请注意,这将增加缓存开销

$dependencies[Cache::SLIDING] = true;

便捷的选项是允许数据在特定文件更改或多个文件之一更改时过期。例如,这可以用于缓存处理这些文件所产生的数据。请使用绝对路径。

$dependencies[Cache::FILES] = '/path/to/data.yaml';
// nebo
$dependencies[Cache::FILES] = ['/path/to/data1.yaml', '/path/to/data2.yaml'];

我们可以让缓存中的某个项在另一个项(或多个项之一)过期时过期。当我们缓存整个HTML页面及其片段时可以使用此功能。一旦片段更改,整个页面就变得无效。如果我们有存储在如 frag1frag2 等键下的片段,我们将使用

$dependencies[Cache::ITEMS] = ['frag1', 'frag2'];

也可以使用自定义函数或静态方法来控制过期,这些方法在读取时始终决定项是否仍然有效。例如,我们可以让项在PHP版本更改时过期。我们将创建一个函数,比较当前版本与参数,并在保存时将一个数组以 [函数名, ...参数] 的形式添加到依赖项中

function checkPhpVersion($ver): bool
{
	return $ver === PHP_VERSION_ID;
}

$dependencies[Cache::CALLBACKS] = [
	['checkPhpVersion', PHP_VERSION_ID] // expire when checkPhpVersion(...) === false
];

当然,所有标准都可以组合。当至少有一个标准不满足时,缓存将过期。

$dependencies[Cache::EXPIRE] = '20 minutes';
$dependencies[Cache::FILES] = '/path/to/data.yaml';

使用标签进行失效

标签是一个非常有用的失效工具。我们可以将一系列标签(任意字符串)分配给缓存中存储的每个项。例如,假设我们有一个包含文章和评论的HTML页面,我们想要缓存。因此,我们在保存到缓存时指定标签

$dependencies[Cache::TAGS] = ["article/$articleId", "comments/$articleId"];

现在,让我们转到管理界面。这里有一个用于编辑文章的表单。在将文章保存到数据库的同时,我们调用 clean() 命令,这将通过标签删除缓存项

$cache->clean([
	Cache::TAGS => ["article/$articleId"],
]);

同样,在添加新评论(或编辑评论)的地方,我们不会忘记使相关的标签失效

$cache->clean([
	Cache::TAGS => ["comments/$articleId"],
]);

我们取得了什么成果?那就是我们的HTML缓存将在文章或评论更改时失效(删除)。当编辑ID为10的文章时,标签 article/10 被强制失效,并从缓存中删除携带该标签的HTML页面。在相关文章下插入新评论时也会发生相同的情况。

标签需要 Journal

按优先级失效

我们可以为缓存中的单个项设置优先级,并且当,例如,缓存超过一定大小时,将可以以受控的方式删除它们

$dependencies[Cache::PRIORITY] = 50;

删除优先级等于或小于100的所有项

$cache->clean([
	Cache::PRIORITY => 100,
]);

优先级需要所谓的 Journal

清除缓存

Cache::ALL 参数清除所有内容

$cache->clean([
	Cache::ALL => true,
]);

批量读取

对于批量读取和写入缓存,我们使用 bulkLoad() 方法,其中我们传递一个键数组并获取一个值数组

$values = $cache->bulkLoad($keys);

bulkLoad() 方法与 load() 类似,具有第二个回调参数,该参数传递生成项的键

$values = $cache->bulkLoad($keys, function ($key, &$dependencies) {
	$computedValue = ...; // heavy computations
	return $computedValue;
});

输出缓存

输出可以非常优雅地捕获和缓存

if ($capture = $cache->start($key)) {

	echo ... // printing some data

	$capture->end(); // save the output to the cache
}

如果输出已在缓存中,则 start() 方法打印它并返回 null,因此条件不会执行。否则,它开始缓冲输出,并返回 $capture 对象,我们可以使用该对象将数据最终保存到缓存中。

存储

存储是表示数据物理存储位置的对象。我们可以使用数据库、Memcached服务器或最可用的存储,即磁盘上的文件。

FileStorage

将缓存写入磁盘文件。存储 Sura\Cache\Storages\FileStorage 在性能方面进行了很好的优化,并且最重要的是确保操作的全原子性。这是什么意思呢?这意味着在使用缓存时,不可能读取另一个线程尚未完全写入的文件,或者有人会在你手中删除它。因此,使用缓存是完全安全的。

此存储还具有一个重要的内置功能,可以防止在清除缓存或冷缓存(即未创建)时CPU使用率急剧增加。这是 缓存踩踏 防止。有时,在某个时刻,有多个并发请求需要从缓存中获取相同的内容(例如,昂贵的SQL查询的结果),由于没有缓存,所有进程开始执行相同的SQL查询。处理器负载会加倍,甚至可能发生没有任何线程能在时间限制内响应的情况,缓存未创建,应用程序崩溃。幸运的是,Sura中的缓存是这样工作的,当有多个并发请求对同一项进行请求时,它只由第一个线程生成,其他线程等待,然后使用生成的结果。

创建FileStorage的示例

// the storage will be the directory '/path/to/temp' on the disk
$storage = new Sura\Cache\Storages\FileStorage('/path/to/temp');

MemcachedStorage

服务器 Memcached 是一个高性能的分布式存储系统,其适配器是 Sura\Cache\Storages\MemcachedStorage

需要PHP扩展 memcached

$storage = new Sura\Cache\Storages\MemcachedStorage('10.0.0.158');

MemoryStorage

Sura\Cache\Storages\MemoryStorage 是一种将数据存储在PHP数组中的存储,因此当请求结束时数据会丢失。

$storage = new Sura\Cache\Storages\MemoryStorage;

SQLiteStorage

SQLite数据库和适配器 Sura\Cache\Storages\SQLiteStorage 提供了一种在单个磁盘文件中缓存的方法。配置将指定此文件的路径。

需要PHP扩展 pdopdo_sqlite

$storage = new Sura\Cache\Storages\SQLiteStorage('/path/to/cache.sdb');

DevNullStorage

一种特殊的存储实现是 Sura\Cache\Storages\DevNullStorage,实际上并不存储任何数据。因此,它适用于测试我们想要消除缓存效果的情况。

$storage = new Sura\Cache\Storages\DevNullStorage;

Journal

Sura 将标签和优先级存储在所谓的日志中。默认情况下,使用SQLite和文件 journal.s3db,并且需要 PHP扩展 pdopdo_sqlite