在 PHP 的开发实践中,APCu 扩展通常被用于在请求之间共享数据。然而,由于 PHP 的共享内存机制受限于其运行模型(尤其是 FPM 或 Apache 的多进程模式),开发者时常对“多进程共享缓存”产生误解。本文将通过 apcu_entry 函数,探索如何在典型的多进程程序中实现 APCu 缓存的共享使用。
APCu(Alternative PHP Cache User)是 PHP 的用户缓存模块,基于共享内存实现数据缓存。它适用于 CLI 模式和 Web 模式,但有一个限制:在多进程模型下,每个 PHP 进程之间不共享变量空间,APCu 的共享是依赖于内存映射的。
这意味着,只要 PHP 使用的是同一个 APCu 内存区域,所有进程就可以访问这个共享内存中的缓存数据。虽然变量作用域是隔离的,但缓存不是。
apcu_entry 是 PHP 5.5+ 引入的函数,用于获取或生成缓存项。它的典型用法如下:
$value = apcu_entry('cache_key', function() {
return some_expensive_function();
});
其工作原理为:
检查 cache_key 是否存在;
如果存在,直接返回;
如果不存在,执行闭包中的函数,将结果缓存,并返回。
这种“惰性计算+缓存”的策略非常适合在多进程下的资源共享需求。
以 PHP 的 pcntl_fork 为例,我们模拟一个多进程场景,验证缓存是否共享:
<?php
if (!extension_loaded('apcu')) {
die("APCu 扩展未启用\n");
}
apcu_clear_cache();
$pid = pcntl_fork();
if ($pid == -1) {
die("无法创建子进程\n");
} elseif ($pid === 0) {
// 子进程
$data = apcu_entry('shared_key', function () {
echo "子进程:缓存未命中,生成数据\n";
return '子进程写入的数据 ' . time();
});
echo "子进程读取缓存:$data\n";
} else {
// 父进程
sleep(1); // 等待子进程先执行
$data = apcu_entry('shared_key', function () {
echo "父进程:缓存未命中,生成数据\n";
return '父进程写入的数据 ' . time();
});
echo "父进程读取缓存:$data\n";
}
通常你会看到:
子进程:缓存未命中,生成数据
子进程读取缓存:子进程写入的数据 1715600000
父进程读取缓存:子进程写入的数据 1715600000
这说明 APCu 的缓存内容确实在父子进程之间共享。
确保 PHP 使用的是同一 SAPI(例如 CLI 模式运行多个子进程);
确认 apc.enable_cli=1 在 php.ini 中已启用,否则 CLI 模式下不会启用 APCu;
APCu 不适用于多服务器缓存共享(例如负载均衡下的多个 PHP 实例),如需多机缓存,请使用 Redis、Memcached 等服务;
避免过度依赖缓存的持久性,APCu 是进程级缓存,重启 PHP-FPM 会导致数据丢失。
假设我们从远程 API 获取配置数据:
$config = apcu_entry('remote_config', function () {
echo "从远程接口获取数据...\n";
$json = file_get_contents('https://gitbox.net/api/config');
return json_decode($json, true);
});
只要缓存未过期,任何进程调用这段逻辑都不会重复请求接口。这极大提高了性能,并减少了远程调用次数。
apcu_entry 提供了一个优雅的缓存机制,特别适用于“计算成本高或调用代价大”的场景。在多进程 PHP 程序中,只要运行环境允许,APCu 的缓存内容可以在进程间共享。理解其运行原理和限制,将帮助你更高效地构建高性能 PHP 应用。