Workerman 不直接支持 /dev/shm 缓存,因其内置 cache 组件基于磁盘设计,绕过抽象层用原生文件 I/O 才能发挥 tmpfs 低延迟优势,但需手动处理并发、过期与安全。
Workerman 本身不直接支持 /dev/shm 作为缓存后端,但你可以绕过框架封装,用 PHP 原生文件 I/O 直接读写,实现比 Redis/Memcached 更低延迟、零网络开销的本地临时缓存——前提是你的数据体积小、生命周期短、且不需要跨进程强一致性。
为什么不能直接用 Workerman 的 cache->set() 写 /dev/shm
Workerman 自带的
组件(如
)默认基于普通磁盘目录,它会做序列化、目录结构管理、过期扫描等,这些操作在
上不仅没收益,反而因锁竞争和 stat 频繁拖慢速度。它的设计目标是“兼容性”,不是“极致内存 I/O”。
真正发挥
价值的方式,是跳过所有抽象层,直接用
、
、
操作文件:
是 tmpfs,所有内容驻留内存,
/
等价于 memcpy,无磁盘寻道
没有 journal、noatime 默认启用、无 ext4 inode 开销,单次小文件读写常低于 10μs
但不支持原子 rename 跨设备(
是独立挂载点),所以不能照搬 FileCache 的“写临时文件 + rename”策略
手动实现 /dev/shm 缓存的关键步骤
核心逻辑:用 key 做文件名,内容直写,靠
控制并发,用
判断过期(避免额外元数据存储)。
Workerman
最新主版本,继续强化高性能异步网络能力,适合 WebSocket、HTTP 服务、长连接、IM、IoT 等场景。
本次版本主要围绕稳定性、协议兼容以及连接处理能力优化,依旧保持超轻量和纯 PHP 特性。
适用于 PHP 8.x 环境,可配合 webman、GatewayWorker 等生态使用。
下载
先检查
是否可用:
,并确认剩余空间(
)——tmpfs 用满会导致 OOM killer 干掉进程
key 必须转义:禁止路径遍历,推荐
,长度建议 ≤ 255 字节
写入时加写锁:
读取时只加读锁或不加锁(若业务能容忍短暂脏读),用
+
校验 TTL,过期则
不要依赖
:tmpfs 下
很快,但频繁调用仍伤性能;可考虑内存中缓存 mtime 副本(需注意进程间不同步)
与 Redis/Memcached 对比的真实瓶颈点
很多人以为 “内存就一定快”,但在 Workerman 场景下,
的优势有严格前提:
单机单 Worker 进程高频访问同一 key:此时
免网络、免序列化、免 Redis 协议解析,实测比本地 Redis 快 2–3 倍
但多 Worker 进程并发读写同一 key 时,
成为串行瓶颈,而 Redis 的原子命令(
、
)天然支持并发
若 value > 1MB,
会吃掉大量内存页,可能触发内核 reclaim,反而比 Redis 的 slab 分配更不稳定
没有 LRU 驱逐机制:必须自己轮询
+
清理,而 Redis 可配置
一个极简可用的 shm_cache.php 示例
注意:
没做容量检查,生产环境必须前置
。
最关键的忽略点:/dev/shm 的生命周期绑定于系统启动,但 Workerman 进程重启不会清空它——旧缓存残留可能引发逻辑错误,务必在应用启动时按需
。
/dev/shmcacheWorkerman\Cache\FileCache/dev/shm/dev/shmfopen()flock()file_put_contents()/dev/shmread()write()/dev/shmflock()filemtime()/dev/shmis_writable('/dev/shm')disk_free_space('/dev/shm')preg_replace('/[^a-zA-Z0-9._-]/', '_', $key)$fp = fopen($path, 'c'); flock($fp, LOCK_EX); file_put_contents($path, $data, LOCK_EX); flock($fp, LOCK_UN); fclose($fp);file_get_contents()filemtime()unlink()clearstatcache()stat/dev/shm/dev/shmflock()GETSETINCR/dev/shmscandir('/dev/shm')filemtime()maxmemory-policyfunction shm_get(string $key, int $ttl = 300)
{
$safe_key = preg_replace('/[^a-zA-Z0-9._-]/', '_', $key);
$path = "/dev/shm/{$safe_key}";
if (!file_exists($path)) return false;
if (filemtime($path) < time() - $ttl) {
@unlink($path);
return false;
}
return file_get_contents($path);
}
function shm_set(string $key, string $value, int $ttl = 300): bool
{
$safe_key = pregreplace('/[^a-zA-Z0-9.-]/', '_', $key);
$path = "/dev/shm/{$safe_key}";
$fp = fopen($path, 'c');
if (!$fp) return false;
if (flock($fp, LOCK_EX)) {
file_put_contents($path, $value, LOCK_EX);
flock($fp, LOCK_UN);
}
fclose($fp);
return true;
}
shm_set()disk_free_space('/dev/shm') > strlen($value) + 1024array_map('unlink', glob('/dev/shm/your_prefix_*'))