本文目录导读:

- 文件缓存(简单,适合小型项目)
- 内存缓存(推荐,高性能)
- 本地进程内驻留(仅限 CLI 模式)
- 数据库持久化(最安全,但最慢)
- 共享文件系统/网络文件系统(NFS)
- 会话(Session)驻留(特定场景)
- 总结对比表格
- 最佳实践建议
- 特别提醒:关于“全局变量”的误区
在 PHP 中实现数据驻留(即数据在多个请求之间持久保存),有多种方式,选择哪种取决于你的具体需求(如数据量、是否需要实时同步、性能要求等)。
这里按从简单到复杂、从本地到分布式的顺序,介绍最常见的几种方案:
文件缓存(简单,适合小型项目)
将数据直接写入文件,下次请求时读取。
- 优点:无需额外服务,实现简单。
- 缺点:磁盘 I/O 慢,不适合高并发;数据无过期时间(需自己管理)。
示例代码(使用 serialize):
<?php
$cacheFile = '/tmp/my_data.cache';
// 读取
if (file_exists($cacheFile)) {
$data = unserialize(file_get_contents($cacheFile));
} else {
// 生成数据(模拟从数据库读取)
$data = ['users' => [1, 2, 3], 'timestamp' => time()];
// 写入
file_put_contents($cacheFile, serialize($data));
}
var_dump($data);
内存缓存(推荐,高性能)
这是最常用的方案,PHP-FPM 模式(传统 web)下,PHP 进程在处理完请求后会销毁所有变量,无法直接跨请求驻留,必须依赖外部进程。
1 Redis(首选)
- 优点:速度快、支持丰富的数据结构(List、Hash、Set)、支持过期时间、支持分布式。
- 适用:会话管理、热数据缓存、排行榜、分布式锁。
示例(使用 phpredis 或 Predis):
<?php
// 连接
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
// 写入(带过期时间 3600 秒)
$redis->set('user:1001', json_encode(['name' => 'Alice']), 3600);
// 读取
$user = json_decode($redis->get('user:1001'), true);
// 使用 Hash 存储对象
$redis->hMset('user:1001', ['name' => 'Alice', 'age' => 30]);
$name = $redis->hGet('user:1001', 'name');
2 Memcached
- 优点:简单、极快,内存存储。
- 缺点:只支持字符串类型,无持久化(重启丢失)。
本地进程内驻留(仅限 CLI 模式)
如果你运行的是长期运行的 CLI 脚本(如队列消费者、Workerman/Swoole 常驻进程),PHP 变量在内存中天然驻留。
- 适用:WebSocket 服务器、异步任务队(Resque/Beanstalkd workers)、常驻内存的应用(如 Laravel Octane, Swoole)。
- 注意:需要防止内存泄漏,必须用
unset()清理大对象。
示例(Workerman 服务器,全局变量驻留):
<?php
use Workerman\Worker;
$worker = new Worker('tcp://0.0.0.0:2345');
// 该数组在进程生命周期内一直驻留内存
$globalData = [];
$worker->onMessage = function ($connection, $data) use (&$globalData) {
$globalData[md5($data)] = $data; // 数据驻留
$connection->send("Stored: " . count($globalData));
};
Worker::runAll();
数据库持久化(最安全,但最慢)
将数据写入 MySQL/PostgreSQL,下次请求再查。
- 优点:持久化可靠性最高,支持复杂查询。
- 缺点:磁盘 I/O,性能不如内存。
- 适用:用户数据、订单数据等需要永久保存的业务数据。
共享文件系统/网络文件系统(NFS)
多台机器共享一个文件目录存储缓存(如 APCu over NFS 不可行,需自己实现锁),一般不建议,因为并发锁不好处理,Redis 是更好的选择。
会话(Session)驻留(特定场景)
针对需要跨页面保持的用户级数据(如登录状态),PHP 支持原生 Session。
- 存储位置:默认文件,可改为 Redis(修改
php.ini或用session_set_save_handler())。 - 缺点:作用域仅为单个用户,不适合全局数据。
总结对比表格
| 方法 | 跨请求持久 | 分布式支持 | 性能 | 持久化 | 适用场景 |
|---|---|---|---|---|---|
| 文件缓存 | ✅ | ❌ | 中 | ✅ | 单机小项目 |
| Redis | ✅ | ✅ | 极高 | ✅ (RDB/AOF) | 大多数生产环境首选 |
| Memcached | ✅ | ✅ | 极高 | ❌ | 纯热缓存,不介意丢失 |
| 数据库表 | ✅ | ✅ | 低-中 | ✅ | 核心业务数据 |
| PHP常驻进程 | ✅ | ❌ | 极高 | ❌ | Swoole/Workerman/CLI |
| Session | ✅ | ✅ | 中 | ✅ | 特定用户状态 |
最佳实践建议
- 热数据(排行榜、验证码、API Token) -> Redis。
- 静态配置(如系统配置参数) -> APCu(PHP内置,单机进程内驻留,共享内存)或 文件。
- 业务数据(订单、用户资料) -> 数据库(使用主从复制增加读性能)。
- 如果项目使用框架(如 Laravel、Symfony)-> 直接用框架内置的 Cache Facade(底层就支持 Redis/File/Database)。
特别提醒:全局变量”的误区
在传统 Apache/Nginx + PHP-FPM 下,普通的 PHP 全局变量 $GLOBALS 在本次请求结束后会被销毁,无法跨请求驻留,这是 PHP 生命周期决定的(PHP_FPM 进程虽然常驻,但每次请求都会重新初始化环境)。
注意:
static关键字在函数内对变量驻留有效,但也仅限于当前请求的进程内,请求结束会被释放(FPM模式下)。
如果你想在 FPM 模式下“共享”数据,必须使用共享存储(Redis/文件/数据库),没有第三条路。