Webman中应使用webman/cache扩展的ArrayCache作为本地缓存,配合Redis构成两级缓存:Cache::get自动先查内存再穿透Redis,Cache::set同步写入两级,TTL独立控制,适用于配置、字典等读多写少数据。

Webman 里 Redis 压力大,不是因为缓存用得少,而是没分层——本地缓存(Local Cache)该上就得上,尤其对高频读、低频变的数据,比如配置项、用户权限树、地区列表。它不替代 Redis,而是挡在 Redis 前面,把重复请求拦在进程内。
Local Cache 该用什么组件?别自己 new ArrayCache
Webman 官方推荐用 webman/cache 扩展,它内置支持 ArrayCache(内存级)、FileCache(文件级),且能和 Redis 缓存无缝组合成多级策略。自己手写静态数组或全局变量缓存,会遇到几个硬伤:
- 无法自动失效:改了数据,缓存不会清,除非重启 Worker
- 多进程不共享但也不隔离:PHP-FPM 思维残留,误以为“全局变量=全进程可见”,实际 Webman 多 Worker 是独立进程,
static $cache = []各自一份,互相看不到,还可能因序列化/反序列化出错 - 没 TTL 控制:过期逻辑得自己轮询或依赖外部信号,容易漏
正确做法是安装并配置:composer require webman/cache,然后在 config/cache.php 中启用 ArrayCache 作为一级缓存:
return [
'default' => [
'handler' => \support\Cache\Handler\ArrayCache::class,
'options' => ['ttl' => 60], // 秒级 TTL,非永久
],
];
怎么组合 Local Cache + Redis 构成两级缓存?
两级缓存不是“先查本地再查 Redis”,而是“查本地命中就返回;未命中查 Redis,成功后顺手写回本地”。关键在 get 方法的链式行为,不是手动 if-else 判断。
- 用
Cache::get($key)自动走配置的默认驱动(比如ArrayCache),查不到才穿透到 Redis(需配置 fallback 驱动) - 必须显式配置 fallback:在
config/cache.php中加'fallback' => 'redis',否则ArrayCache查不到就直接返回 null,不会往下走 - 写缓存时注意:只有
Cache::set($key, $value, $ttl)会同时写入当前驱动(本地)和 fallback(Redis);而Redis::set()只写 Redis,绕过本地层
示例场景(获取系统配置):
// config/cache.php 已配好 default=ArrayCache, fallback=redis
$config = Cache::get('system:config:mail'); // 先查内存数组,没命中自动查 Redis 并回填
if ($config === null) {
$config = Db::table('configs')->where('key', 'mail')->value('value');
Cache::set('system:config:mail', $config, 300); // 写两级:内存 + Redis
}
Local Cache 的 TTL 和失效怎么管?
本地缓存的 TTL 不等于 Redis 的 TTL,两者独立控制。这是优势也是坑点:
-
ArrayCache的ttl是进程内计时,靠 PHP 的microtime()算,重启即清空——适合短时、可容忍短暂不一致的场景(如菜单权限) - 如果业务要求“强一致”,比如用户状态变更后必须立刻失效,就不能只靠 TTL,得主动调用
Cache::delete($key)或Cache::clear() - 注意:调用
Cache::delete($key)只删本地缓存,不会同步删 Redis;要删两级,得额外执行Redis::del($key) - 高并发下多个 Worker 同时写同一 key 到本地缓存,不会冲突,但可能造成短暂数据不一致(各进程本地值不同),所以敏感数据慎用本地缓存
哪些数据适合放 Local Cache?哪些坚决不能放?
本地缓存不是万能胶,用错反而放大问题:
- ✅ 适合:配置类(
config:app:name)、字典类(dict:status:1)、统计类(stats:today:pv)、权限白名单(perm:user:123)——读多写少、变更频率低、允许秒级延迟 - ❌ 绝对避免:用户会话(
session:user:xxx)、订单状态(order:status:9876)、库存数量(stock:sku:001)——这些必须强一致,本地缓存无法跨进程同步,极易导致脏读 - ⚠️ 警惕:带用户 ID 的个性化数据(如
user:profile:123)——单个 Worker 内可能缓存千条,内存暴涨;且用户切换设备/登录态变化时,本地缓存无法感知
真正难的不是怎么配,而是判断哪一层该承担什么职责。Local Cache 的价值不在“快”,而在“减压”——它让 Redis 少接 70% 的重复请求,但代价是放弃部分一致性。这个 trade-off,得由业务逻辑自己拍板。


















