缓存使用指南
本篇介绍通过 Cache 门面完成日常缓存操作:读写删除、过期控制、数值增减、数组集合、多商店切换与竞争锁。驱动选型与配置见 缓存驱动,标签分组见 缓存标签。
基本读写
写入 set()
use Viswoole\Cache\Facade\Cache;
// 未指定过期时间,使用驱动默认值(默认永不过期)
Cache::set('config:site', $config);
// 指定过期时间(秒)
Cache::set('user:1', ['name' => '张三'], 3600);
// NX 模式:仅当键不存在(或已过期)时写入,常用于抢占
Cache::set('job:report:today', $payload, 3600, true);set() 的完整签名:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| key | string | 必填 | 缓存键 |
| value | mixed | 必填 | 缓存值,任意可序列化数据 |
| expire | DateTime|int|null | null | 过期秒数;传 DateTime 表示绝对时间点;null 使用驱动默认值 |
| NX | bool | false | 为 true 时仅当键不存在时才写入 |
读取 get()
// 不存在时返回 null
$user = Cache::get('user:1');
// 不存在时返回默认值
$page = Cache::get('user:1:page', 1);get() 不支持闭包默认值
第二个参数是「默认值」而非回调:即使传入闭包,缓存不存在时也会原样返回闭包对象本身。「不存在时自动计算并回填」需自行 has() 判断后再 set()。
判断与删除
Cache::has('user:1'); // 是否存在(已过期视为不存在)
Cache::delete('user:1'); // 删除单个
Cache::delete(['k1', 'k2', 'k3']); // 批量删除,返回删除数量
Cache::clear(); // 清除当前驱动下的所有缓存clear() 的作用范围
File 驱动的 clear() 会递归清空整个缓存存储目录;Redis 驱动的 clear() 执行的是 flushDB,会清空所配置 db_index 下的全部数据(包括非缓存用途的键)。共享 Redis 实例时请通过 prefix 与独立 db_index 隔离。
读取并删除 pull()
// 原子性不强,等价于 get + delete 的组合,适合一次性消费的场景
$job = Cache::pull('job:pending');剩余有效期 ttl()
$ttl = Cache::ttl('user:1');
// int 剩余秒数
// -1 长期有效(未设置过期时间)
// false 不存在或已过期过期时间
set() 的 $expire 支持三种形态:
Cache::set('a', 1, 600); // 相对秒数
Cache::set('b', 2, new DateTime('+1 hour')); // 绝对时间点
Cache::set('c', 3, 0); // 0 表示永不过期驱动可在配置中设置 expire 作为默认过期时间(见 缓存驱动),此后 set() 不传 $expire 时按默认值处理。
数值增减
针对数值型缓存的自增/自减操作,返回操作后的新值:
Cache::set('views', 100);
Cache::inc('views'); // 101,步长默认 1
Cache::inc('views', 10); // 111
Cache::dec('views', 5); // 106仅适用于数值缓存
File 驱动要求缓存值为 int / float,否则抛出 CacheErrorException;Redis 驱动底层使用 INCRBY / DECRBY,要求值为整数。
cache() 助手函数
全局助手 cache() 用于快速读取缓存;不传键时返回 CacheManager 实例,可继续链式调用任意门面方法:
$value = cache('user:1'); // 等价于 Cache::get('user:1')
$value = cache('user:1', '默认值'); // 带默认值读取
cache()->set('user:2', $data, 60); // 返回 CacheManager,可继续完整操作更多助手函数见 助手函数。
数组集合操作
以集合(Set)语义维护一个字符串数组:追加时自动去重,File 驱动用序列化数组模拟,Redis 驱动直接使用原生 Set 结构:
// 追加(已存在的值不会重复添加),返回新增数量,全部已存在返回 false
Cache::sAddArray('permissions', ['read', 'write']);
Cache::sAddArray('permissions', 'execute');
// 读取全部成员,集合不存在返回 false
$perms = Cache::getArray('permissions');
// 移除成员,返回移除数量
Cache::sRemoveArray('permissions', ['execute']);并发注意
File 驱动的集合操作是「读取-合并-回写」三步,并发写入可能互相覆盖;需要严格原子性的集合场景请使用 Redis 驱动。
多缓存商店
config/cache.php 中可注册多个商店(store),通过 store() 按名称切换,商店名不区分大小写:
// 操作指定商店
Cache::store('redis')->set('hot:key', $value, 60);
// 运行时注册新商店(支持驱动实例 / 类名 / 配置数组三种形式)
Cache::addStore('memcached', new MemcachedDriver());
// 判断商店是否已注册
Cache::hasStore('redis'); // true未调用 store() 的所有静态调用(Cache::set()、Cache::get() 等)均转发到 default 指定的默认商店。
竞争锁(分布式锁)
lock() 用于串行化并发访问同一临界资源,取锁失败会自动重试,重试耗尽抛出 Viswoole\Cache\Exception\CacheErrorException:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| scene | string | 必填 | 锁场景标识,如 order:create |
| expire | int | 10 | 锁过期时间(秒),作为进程崩溃未解锁时的兜底 |
| autoUnlock | bool | false | 驱动连接关闭(close())时是否自动解锁 |
| retry | int | 5 | 取锁失败的重试次数 |
| sleep | int|float | 0.2 | 每次重试的间隔秒数 |
use Viswoole\Cache\Exception\CacheErrorException;
use Viswoole\Cache\Facade\Cache;
try {
// 取锁:10 秒过期,最多重试 5 次,间隔 0.2 秒
$lockId = Cache::lock('order:create', 10);
try {
// 临界区业务,例如防重复下单
} finally {
Cache::unlock($lockId); // 仅锁持有者能解锁
}
} catch (CacheErrorException) {
// 重试耗尽仍未取到锁
}使用要点:
- 必须用
lock()返回的锁 ID 解锁:unlock($id)会在持有记录(Redis 驱动按协程隔离)中校验锁 ID,不匹配或已释放返回false,避免误释放他人的锁。 expire是安全网:即使业务异常且未解锁,锁也会在过期后自动失效,防止死锁。finally中解锁:保证异常路径也能释放锁;autoUnlock仅作为进程级兜底。- Redis 驱动的锁基于
SET NX EX+ Lua 脚本实现「比对锁 ID 再删除」,解锁是原子操作。
最佳实践
Cache-Aside 模式
public function getUserProfile(int $userId): array
{
$cacheKey = "profile:{$userId}";
$cached = Cache::get($cacheKey);
if ($cached !== null) {
return $cached;
}
$profile = $this->profileRepo->find($userId); // 回源数据库
// 空结果也缓存较短时间,缓解缓存穿透
Cache::tag("user:{$userId}")->set($cacheKey, $profile, $profile === null ? 60 : 3600);
return $profile;
}写入时关联标签(tag()),用户资料更新时 Cache::tag("user:{$userId}")->clear() 即可精准失效该用户的全部缓存,详见 缓存标签。
热点行防击穿
对「读库 + 回填缓存」的热点接口,可用 set() 的 NX 模式或 lock() 保证同一时刻只有一个请求回源数据库,其余请求等待或读取旧值。
