StartMVC开发手册

可以快速上手的开发文档

手册目录

缓存高级用法

高级用法

使用文件缓存

文件缓存适合开发环境或小型应用:

$fileCache = new \startmvc\core\Cache('file');

// 存储复杂数据
$fileCache->set('user_data', [
 'id' => 1,
 'name' => '张三',
 'roles' => ['admin', 'editor']
]);

// 获取数据
$userData = $fileCache->get('user_data');
  • 写入带文件锁(LOCK_EX),并发写入不会产生残缺文件;
  • 缓存文件损坏或被篡改时自动清理并视为未命中,不会报错;
  • 读取过期文件时会顺带删除,但长期不访问的过期文件会残留,可定期清理runtime/cache/目录。

使用Redis缓存

Redis缓存适合生产环境和高并发应用:

$redisCache = new \startmvc\core\Cache('redis');

// 存储数组
$user = [
 'id' => 100,
 'name' => '李四'
];
$redisCache->set('user', $user, 600);

// 获取数据
$user = $redisCache->get('user');

注意(v2.9.4变更):反序列化启用了allowed_classes => false白名单以杜绝对象注入攻击,不支持缓存对象(stdClass、模型对象取出后会变成__PHP_Incomplete_Class),请统一存储数组,取出后再组装。

使用Memcached缓存(v2.9.4新增)

Memcached与Redis类似,是更轻量的内存缓存方案,需安装memcachedPHP扩展并启动Memcached服务:

$mem = \startmvc\core\Cache::store('memcached');

$mem->set('key', 'value', 300); // 原生TTL过期
$value = $mem->get('key'); // 未命中返回null

// 即使缓存了false值也能与"未命中"正确区分
$mem->set('flag', false, 60);
var_dump($mem->get('flag')); // bool(false),而非误判为未命中

注意:Memcached不支持按前缀批量删除,clear()清空整个实例上的所有键(含共享该实例的其他应用数据),共享实例时请慎用。

自定义缓存配置

// 自定义Redis配置
$customConfig = [
 'host' => '192.168.1.100',
 'port' => 6380,
 'password' => 'secret',
 'database' => 1,
 'cacheTime' => 7200
];
$cache = new \startmvc\core\Cache('redis', $customConfig);

提示:带自定义参数的实例不进入实例池,每次都会新建连接。常规场景请优先在config/cache.php中配置,并用Cache::store('redis')获取以复用连接。

remember()进阶用法

回调返回null不缓存

框架用null表示"未命中",因此回调返回null不会写入缓存,下次仍会执行回调。可利用这一语义实现"查不到就不缓存":

// 用户不存在时返回null,不缓存空结果,注册后立即可查
$user = $cache->remember('user:' . $uid, function () use ($uid) {
 $user = db('user')->where('uid', $uid)->first();
 return $user ?: null;
}, 3600);
高并发防击穿

remember()无锁,热点key过期瞬间可能有大量请求同时执行回调(缓存击穿)。应对方式:

  • 放宽要求:多数业务允许短暂重复计算,直接使用remember()即可;
  • 加互斥锁:对必须单次执行的重计算(如报表),用Redis的SETNX自行加锁,或改为"后台定时刷新+前端只读"模式;
  • 逻辑过期:缓存长TTL的数据并附带逻辑过期时间,发现逻辑过期后由一个请求触发刷新,其余请求先返回旧值。

最佳实践

  1. 选择合适的驱动:开发环境使用文件缓存简单快捷;生产环境使用Redis或Memcached提高性能和可靠性。
  2. 合理设置缓存时间:频繁变化的数据设置短TTL,相对稳定的数据设置更长的TTL。
  3. 缓存键命名规范:使用有意义的前缀区分不同模块,例如:user:profile:1001、product:list:page1。
  4. 缓存更新策略:数据更新时主动清除相关缓存,下次访问自动重建;对于关联数据,考虑批量清除相关缓存。
  5. 优先使用remember():把"读缓存→查库→写缓存"收敛为一行代码,避免手写get/set遗漏分支。

注意事项

  1. 文件缓存会在runtime/cache/目录下生成缓存文件,确保该目录可写(框架会自动创建)。
  2. Redis/Memcached需先安装对应PHP扩展(redis/memcached)并启动服务,否则实例化时抛出异常。
  3. 缓存键名区分大小写。
  4. 缓存数据会自动序列化和反序列化,支持存储数组和标量;对象、闭包、资源不可缓存
  5. null与"未命中"等价,不要缓存null值(可利用该语义实现"查不到不缓存",见上文remember()进阶用法)。

v2.9.4升级对照

项目2.9.3及以前2.9.4起
set()返回值$this,支持链式调用bool(是否写入成功),不再支持链式
cache()第3参数默认固定3600,显式传3600会被吞掉改用驱动默认值默认null(用驱动cacheTime);显式传入任意值均正常生效
缓存对象Redis驱动可以(存在反序列化注入风险)一律不支持(allowed_classes => false安全白名单),请存数组
Cache::store()每次调用都新建实例和连接同驱动复用实例(连接池化)
remember()新增:get-or-set,未命中执行回调并缓存
Memcached驱动新增:Cache::store('memcached')

通过合理使用缓存系统,可以显著提升应用性能,减少数据库查询,提高用户体验。