缓存系统

最后更新: 2026-01-27 11:02:47

缓存系统

Unicode Framework 提供了灵活的缓存系统,支持文件缓存、Redis 缓存和标签缓存。

缓存基础

缓存接口

所有缓存实现都遵循 CacheInterface 接口:

use Unicode\Framework\Interfaces\CacheInterface;

interface CacheInterface { public function get(string $key, mixed $default = null): mixed; public function set(string $key, mixed $value, ?int $ttl = null): bool; public function delete(string $key): bool; public function clear(): bool; public function has(string $key): bool; }

文件缓存

基本使用

use Unicode\Framework\Cache\FileCache;

$cache = new FileCache( directory: 'runtime/cache', defaultTtl: 3600 );

// 设置缓存 $cache->set('user:1', $userData, 3600);

// 获取缓存 $userData = $cache->get('user:1');

// 删除缓存 $cache->delete('user:1');

// 检查缓存是否存在 if ($cache->has('user:1')) { $userData = $cache->get('user:1'); }

配置

$cache = new FileCache(
    directory: 'runtime/cache',  // 缓存目录
    defaultTtl: 3600,           // 默认过期时间(秒)
    prefix: 'app:',             // 键前缀
);

Redis 缓存

基本使用

use Unicode\Framework\Cache\RedisCache;

$cache = new RedisCache( dsn: 'redis://127.0.0.1:6379', password: null, database: 1, prefix: 'cache:', defaultTtl: 3600 );

// 设置缓存 $cache->set('user:1', $userData, 3600);

// 获取缓存 $userData = $cache->get('user:1');

// 删除缓存 $cache->delete('user:1');

配置

$cache = new RedisCache(
    dsn: 'redis://127.0.0.1:6379',  // Redis DSN
    password: 'your-password',       // Redis 密码(可选)
    database: 1,                     // 数据库编号
    prefix: 'cache:',               // 键前缀
    defaultTtl: 3600,               // 默认过期时间(秒)
);

DSN 格式

redis://[password@]host[:port][/database]
redis://127.0.0.1:6379
redis://password@127.0.0.1:6379/1

标签缓存

标签缓存允许您为一组相关的缓存项添加标签,然后批量清除。

基本使用

use Unicode\Framework\Cache\TaggedCache;

// 创建标签缓存(需要底层缓存支持) $taggedCache = new TaggedCache($cache, ['users']);

// 设置带标签的缓存 $taggedCache->set('user:1', $userData, 3600); $taggedCache->set('user:2', $userData2, 3600);

// 清除该标签的所有缓存 $taggedCache->flush(); // 清除所有 'users' 标签的缓存

多标签

$taggedCache = new TaggedCache($cache, ['users', 'active']);

// 设置缓存(带有 users 和 active 两个标签) $taggedCache->set('user:1', $userData, 3600);

// 清除任一标签的所有缓存 $taggedCache->flush(); // 清除所有 users 或 active 标签的缓存

缓存操作

设置缓存

// 使用默认 TTL
$cache->set('key', 'value');

// 指定 TTL(秒) $cache->set('key', 'value', 3600);

// 永久缓存(不设置 TTL) $cache->set('key', 'value', null);

获取缓存

// 获取缓存
$value = $cache->get('key');

// 获取缓存,如果不存在返回默认值 $value = $cache->get('key', 'default');

// 获取缓存,如果不存在执行回调 $value = $cache->get('key', function() { return expensiveOperation(); });

删除缓存

// 删除单个缓存
$cache->delete('key');

// 删除多个缓存 $cache->delete('key1'); $cache->delete('key2');

// 清空所有缓存 $cache->clear();

检查缓存

// 检查缓存是否存在
if ($cache->has('key')) {
    $value = $cache->get('key');
}

获取或设置

// 如果缓存不存在,执行回调并缓存结果
$value = $cache->get('key', function() {
    $data = expensiveOperation();
    $cache->set('key', $data, 3600);
    return $data;
});

缓存模式

缓存穿透

缓存穿透是指查询一个不存在的数据,导致每次查询都访问数据库。

解决方案
// 缓存空值
$user = $cache->get('user:999', function() {
    $user = db('users')->where('id', 999)->first();
    if ($user === null) {
        // 缓存空值,设置较短的过期时间
        $cache->set('user:999', null, 60);
        return null;
    }
    $cache->set('user:999', $user, 3600);
    return $user;
});

缓存击穿

缓存击穿是指热点数据过期,大量请求同时访问数据库。

解决方案
// 使用锁机制
$lockKey = 'lock:user:1';
if ($cache->has($lockKey)) {
    // 等待其他请求完成
    sleep(0.1);
    return $cache->get('user:1');
}

// 设置锁 $cache->set($lockKey, true, 10);

try { $user = db('users')->where('id', 1)->first(); $cache->set('user:1', $user, 3600); } finally { $cache->delete($lockKey); }

缓存雪崩

缓存雪崩是指大量缓存同时过期,导致大量请求访问数据库。

解决方案
// 为缓存过期时间添加随机值
$ttl = 3600 + rand(0, 600); // 3600-4200 秒
$cache->set('key', $value, $ttl);

最佳实践

1. 缓存键命名

// ✅ 推荐:使用有意义的键名
$cache->set('user:1', $user);
$cache->set('user:1:profile', $profile);
$cache->set('post:123:comments', $comments);

// ❌ 错误:使用无意义的键名 $cache->set('a1', $user); $cache->set('b2', $profile);

2. 缓存过期时间

// ✅ 推荐:根据数据更新频率设置过期时间
$cache->set('user:1', $user, 3600);        // 用户数据:1小时
$cache->set('config:app', $config, 86400); // 配置数据:24小时
$cache->set('stats:daily', $stats, 3600);  // 统计数据:1小时

// ❌ 错误:所有数据使用相同的过期时间 $cache->set('user:1', $user, 3600); $cache->set('config:app', $config, 3600); // 配置数据不应该频繁过期

3. 缓存更新策略

// ✅ 推荐:更新数据时同时更新缓存
public function updateUser(int $id, array $data): void
{
    db('users')->where('id', $id)->update($data);

// 更新缓存 $user = db('users')->where('id', $id)->first(); $cache->set("user:{$id}", $user, 3600);

// 清除相关缓存 $taggedCache = new TaggedCache($cache, ['users']); $taggedCache->flush(); }

// ❌ 错误:只更新数据库,不更新缓存 public function updateUser(int $id, array $data): void { db('users')->where('id', $id)->update($data); // 缓存仍然是旧数据 }

4. 使用标签缓存

// ✅ 推荐:使用标签缓存管理相关数据
$taggedCache = new TaggedCache($cache, ['users']);

// 设置用户相关缓存 $taggedCache->set('user:1', $user1, 3600); $taggedCache->set('user:2', $user2, 3600); $taggedCache->set('user:list', $userList, 3600);

// 用户数据更新时,清除所有用户相关缓存 $taggedCache->flush();

5. 缓存预热

// ✅ 推荐:应用启动时预热常用数据
public function warmUpCache(): void
{
    // 预热配置缓存
    $config = db('config')->get();
    $cache->set('config:all', $config, 86400);

// 预热热门数据 $hotUsers = db('users') ->where('status', 'active') ->orderBy('views', 'desc') ->limit(100) ->get();

foreach ($hotUsers as $user) { $cache->set("user:{$user['id']}", $user, 3600); } }

6. 缓存监控

// ✅ 推荐:监控缓存命中率
class CacheMonitor
{
    private int $hits = 0;
    private int $misses = 0;

public function get(string $key, callable $callback): mixed { if ($cache->has($key)) { $this->hits++; return $cache->get($key); }

$this->misses++; $value = $callback(); $cache->set($key, $value, 3600); return $value; }

public function getHitRate(): float { $total = $this->hits + $this->misses; return $total > 0 ? ($this->hits / $total) * 100 : 0; } }

常见问题

Q: 如何选择缓存驱动?

A:

  • 文件缓存:适合小型应用,不需要额外服务
  • Redis 缓存:适合中大型应用,需要高性能和分布式支持
  • Q: 缓存键冲突怎么办?

    A: 使用前缀区分不同模块的缓存:

    $userCache = new FileCache('runtime/cache', 3600, 'user:');
    $configCache = new FileCache('runtime/cache', 86400, 'config:');
    

    Q: 如何清除所有缓存?

    A: 使用 clear() 方法:

    $cache->clear();
    

    Q: 缓存数据如何序列化?

    A: 框架自动处理序列化,支持字符串、数组、对象等数据类型。

    Q: 如何实现缓存分布式?

    A: 使用 Redis 缓存,Redis 天然支持分布式。

    相关文档

  • 配置管理 - 缓存配置
  • 性能优化 - 缓存优化建议