在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 應用。