nette / caching
⏱ Nette 缓存:具有易于使用的 API 和许多缓存后端的库。
Requires
- php: 8.0 - 8.4
- nette/utils: ^4.0
Requires (Dev)
- latte/latte: ^2.11 || ^3.0.12
- nette/di: ^3.1 || ^4.0
- nette/tester: ^2.4
- phpstan/phpstan: ^1.0
- psr/simple-cache: ^2.0 || ^3.0
- tracy/tracy: ^2.9
Suggests
- ext-pdo_sqlite: to use SQLiteStorage or SQLiteJournal
Conflicts
- latte/latte: >=3.0.0 <3.0.12
- dev-master / 4.0.x-dev
- v3.3.x-dev
- v3.3.1
- v3.3.0
- v3.2.x-dev
- v3.2.3
- v3.2.2
- v3.1.x-dev
- v3.1.4
- v3.1.3
- v3.1.2
- v3.1.1
- v3.1.0
- v3.0.x-dev
- v3.0.2
- v3.0.1
- v3.0.0
- v2.5.x-dev
- v2.5.9
- v2.5.8
- v2.5.7
- v2.5.6
- v2.5.5
- v2.5.4
- v2.5.3
- v2.5.2
- v2.5.1
- v2.5.0
- v2.4.x-dev
- v2.4.7
- v2.4.6
- v2.4.5
- v2.4.4
- v2.4.3
- v2.4.2
- v2.4.1
- v2.4.0
- v2.3.x-dev
- v2.3.5
- v2.3.4
- v2.3.3
- v2.3.2
- v2.3.1
- v2.3.0
- 2.2.x-dev
- v2.2.7
- v2.2.6
- v2.2.5
- v2.2.4
- v2.2.3
- v2.2.2
- v2.2.1
- v2.2.0
This package is auto-updated.
Last update: 2024-09-13 20:33:16 UTC
README
介绍
缓存通过存储数据(一旦难以检索)来加速您的应用程序,以便将来使用。
文档可以在网站上找到。
支持我
你喜欢 Nette 缓存吗?你在期待新功能吗?
谢谢!
安装
composer require nette/caching
它需要 PHP 版本 8.1,并支持 PHP 8.4。
基本用法
与缓存工作的中心是对象 Nette\Caching\Cache。我们创建其实例,并将所谓的存储作为参数传递给构造函数。这表示数据将实际存储的位置(数据库、Memcached、磁盘上的文件等)。你可以在存储部分找到所有基本内容。
对于下面的示例,假设我们有一个别名 Cache
和存储在变量 $storage
中。
use Nette\Caching\Cache; $storage // instance of Nette\Caching\IStorage
实际上,缓存是一个 键值存储,所以我们像关联数组一样在键下读取和写入数据。应用程序由多个独立的部分组成,如果它们都使用一个存储(例如:磁盘上的一个目录),那么迟早会发生键冲突。Nette 框架通过将整个空间划分为命名空间(子目录)来解决这个问题。程序的每个部分随后使用其独特的空间和名称,从而不会发生冲突。
空间名称是作为 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
过期 & 无效化
使用缓存时,必须解决这样一个问题:一些之前保存的数据将随着时间的推移而变得无效。Nette 框架提供了一个机制,如何限制数据的有效性以及如何以受控的方式删除它们(“使它们无效”,使用框架术语)。
数据的有效性是在使用方法的第三个参数保存时设置的,例如:
$cache->save($key, $value, [ Cache::Expire => '20 minutes', ]);
或者使用通过引用传递给 load()
方法回调函数的 $dependencies
参数,例如
$value = $cache->load($key, function (&$dependencies) { $dependencies[Cache::Expire] = '20 minutes'; return ...; ]);
或者使用 load()
方法的第三个参数,例如
$value = $cache->load($key, function () { return ...; ], [Cache::Expire => '20 minutes']);
在以下示例中,我们将假设第二种变体,即存在一个变量 $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页面及其片段时,可以使用此功能。一旦片段更改,整个页面就变为无效。如果我们有存储在键如 frag1
和 frag2
下的片段,我们将使用
$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
对象,我们可以使用该对象最终将数据保存到缓存中。
Latte 中的缓存
在模板中使用Latte缓存非常简单,只需用标签{cache}...{/cache}
包裹模板的一部分。当源模板发生变化时(包括{cache}
标签内的任何包含模板),缓存将自动失效。标签{cache}
可以嵌套,当嵌套块失效(例如,由标签触发)时,父块也会失效。
在标签中可以指定缓存将绑定的键(此处为变量$id
)以及设置过期时间和失效标签。
{cache $id, expire => '20 minutes', tags => [tag1, tag2]} ... {/cache}
所有参数都是可选的,因此您不必指定过期时间、标签或键。
缓存的使用也可以通过if
来条件化——只有当条件满足时,内容才会被缓存
{cache $id, if => !$form->isSubmitted()} {$form} {/cache}
存储
存储是一个表示数据物理存储位置的对象。我们可以使用数据库、Memcached服务器或最易用的存储,即磁盘上的文件。
FileStorage
将缓存写入磁盘上的文件。存储Nette\Caching\Storages\FileStorage
对性能进行了非常好的优化,并且首先确保了操作的全原子性。这意味着当使用缓存时,不会发生我们读取尚未由另一个线程完全写入的文件,或者有人会“在你的手中”删除它的情况。因此,使用缓存是完全安全的。
此存储还有一个重要的内置功能,可以防止在清除缓存或缓存冷(即未创建)时CPU使用率急剧增加。这是缓存雪崩的预防。有时候,会有多个并发请求需要从缓存中获取相同的内容(例如,昂贵SQL查询的结果),因为没有缓存,所有进程都会开始执行相同的SQL查询。处理器负载会加倍,甚至可能发生没有线程在时间限制内响应的情况,缓存没有创建,应用崩溃。幸运的是,Nette的缓存工作方式是这样的,当有多个并发请求对一个项目时,它只由第一个线程生成,其他线程等待,然后使用生成的结果。
创建FileStorage的示例
// the storage will be the directory '/path/to/temp' on the disk $storage = new Nette\Caching\Storages\FileStorage('/path/to/temp');
MemcachedStorage
Memcached服务器是一个高性能的分布式存储系统,其适配器为Nette\Caching\Storages\MemcachedStorage
。
需要PHP扩展memcached
。
$storage = new Nette\Caching\Storages\MemcachedStorage('10.0.0.158');
MemoryStorage
Nette\Caching\Storages\MemoryStorage
是一个将数据存储在PHP数组中的存储,因此当请求结束时数据将丢失。
$storage = new Nette\Caching\Storages\MemoryStorage;
SQLiteStorage
SQLite数据库和适配器Nette\Caching\Storages\SQLiteStorage
提供了一个在磁盘上的单个文件中缓存的方法。配置将指定此文件的路径。
需要PHP扩展pdo
和pdo_sqlite
。
$storage = new Nette\Caching\Storages\SQLiteStorage('/path/to/cache.sdb');
DevNullStorage
存储的特殊实现是Nette\Caching\Storages\DevNullStorage
,它实际上并不存储任何数据。因此,它适用于测试,如果我们想消除缓存的影响。
$storage = new Nette\Caching\Storages\DevNullStorage;
Journal
Nette将标签和优先级存储在所谓的日志中。默认情况下,SQLite和文件journal.s3db
用于此目的,并且需要PHP扩展pdo
和pdo_sqlite
。
如果你喜欢Nette,请现在捐赠。谢谢!