边缘缓存模式 - Cache-Aside Pattern
描述
从存储器中需要的数据加载到缓存。
背景和问题
应用程序使用缓存能优化重复访问存储器数据的问题。但是缓存和存储器的数据可能不一致。
解决方案
读缓存
先读cache,再读db。
1. 如果cache里有数据,直接返回数据。
2. 如果cache里没有数据,那么访问db,并且把数据写到缓存中。
更新缓存
双删缓存策略,最大限度保障数据的一致性。 | |
1. 删除cache。 | |
2. 更新 db。 | |
3. 再删除 cache。 |
注意事项
- 缓存的生命周期(有效期TTL)。
- 缓存淘汰策略。一般采用最近最少使用的策略,但是淘汰缓存是很耗资源的,最好的淘汰策略就是不用启动缓存淘汰机制。
- 预分配缓存策略。
- 缓存和储存器数据之间的一致性策略。
- 使用共享缓存或者分布式缓存而不是本地缓存。
结构中包含的角色
- Cache 抽象缓存接口
- Storage 抽象仓库接口
- RedisCache 具体缓存类
- MysqlStorage 具体仓库类
- CacheAsideManager 边缘缓存管理者
最小可表达代码 - 桥接模式实现
interface Cache | |
{ | |
public function get($key); | |
public function set($key, $value, $tll = null); | |
public function del($key); | |
} | |
interface Storage | |
{ | |
public function get($id); | |
public function update($id, $value); | |
} | |
class RedisCache implements Cache | |
{ | |
private $data = []; // 没别的意思,模拟效果而已 | |
public function get($key) | |
{ | |
return $this->data[$key] ?? null; | |
} | |
public function set($key, $value, $tll = null) | |
{ | |
$this->data[$key] = $value; | |
} | |
public function del($key) | |
{ | |
unset($this->data[$key]); | |
} | |
} | |
class MysqlStorage implements Storage | |
{ | |
private $data = [ | |
['name' => '张三'], | |
]; | |
public function get($id) | |
{ | |
return $this->data[$id] ?? null; | |
} | |
public function update($id, $value) | |
{ | |
$this->data[$id] = $value; | |
} | |
} | |
class CacheAsideManager | |
{ | |
private $cache; | |
private $storage; | |
private const TTL = 3600; | |
private const CACHE_KEY_TEMPLATE = 'test:{key}'; | |
public function __construct(Cache $cache, Storage $storage) | |
{ | |
$this->cache = $cache; | |
$this->storage = $storage; | |
} | |
public function get($id) | |
{ | |
$key = $this->getCacheKey($id); | |
$data = $this->cache->get($key); | |
if ($data) { | |
var_dump('获取缓存'); | |
return $data; | |
} | |
$data = $this->storage->get($id); | |
if ($data) { | |
var_dump('设置缓存'); | |
$this->cache->set($key, $data, self::TTL); | |
} | |
var_dump('获取原数据'); | |
return $data; | |
} | |
public function update($id, $value) | |
{ | |
$key = $this->getCacheKey($id); | |
$this->cache->del($key); | |
$this->storage->update($id, $value); | |
$this->cache->del($key); | |
} | |
protected function getCacheKey($id) | |
{ | |
return strtr(self::CACHE_KEY_TEMPLATE, ['{key}' => $id]); | |
} | |
} | |
$id = 0; | |
$cacheAsideManager = new CacheAsideManager(new RedisCache, new MysqlStorage); | |
$data = $cacheAsideManager->get($id); | |
$data = $cacheAsideManager->get($id); | |
var_dump($data); | |
$cacheAsideManager->update($id, [ | |
'name' => '李四', | |
]); | |
$data = $cacheAsideManager->get($id); | |
var_dump($data); |