我有一个用Laravel编写的API。其中包含以下代码:
public function getData($cacheKey)
{
if(Cache::has($cacheKey)) {
return Cache::get($cacheKey);
}
// if cache is empty for the key, get data from external service
$dataFromService = $this->makeRequest($cacheKey);
$dataMapped = array_map([$this->transformer, 'transformData'], $dataFromService);
Cache::put($cacheKey, $dataMapped);
return $dataMapped;
}
在getData()中,如果缓存包含必要的密钥,则从缓存返回数据。 如果缓存没有必要的密钥,则从外部API获取数据,处理并放置到缓存中,然后返回。
问题是:当方法有很多并发请求时,数据已损坏。我想,由于竞争条件,数据被写入缓存不正确。
答案 0 :(得分:1)
您似乎遇到了某种关键部分问题。但事情就是这样。 Redis操作是原子操作,但Laravel在调用Redis之前会自己进行检查。
这里的主要问题是所有并发请求都将导致请求,然后所有这些请求都会将结果写入缓存(这绝对不是好事)。我建议在代码上实现一个简单的互斥锁。
使用以下内容替换当前的方法主体:
public function getData($cacheKey)
{
$mutexKey = "getDataMutex";
if (!Redis::setnx($mutexKey,true)) {
//Already running, you can either do a busy wait until the cache key is ready or fail this request and assume that another one will succeed
//Definately don't trust what the cache says at this point
}
$value = Cache::rememberForever($cacheKey, function () { //This part is just the convinience method, it doesn't change anything
$dataFromService = $this->makeRequest($cacheKey);
$dataMapped = array_map([$this->transformer, 'transformData'], $dataFromService);
return $dataMapped;
});
Redis::del($mutexKey);
return $value;
}
setnx
是一个本机redis命令,如果它已经存在,则设置一个值。这是以原子方式完成的,因此可用于实现简单的锁定机制,但如果您使用的是redis群集,则(如手册中所述)将无效。在这种情况下,redis手册描述了a method to implement distributed locks
答案 1 :(得分:0)
最后我得到了以下解决方案:我使用Laravel 5.5帮助程序中的retry()函数来获取缓存值,直到它被正常写入,间隔为1秒。