我有一个用 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 获取数据,处理并放入缓存,然后返回。
问题是:当对该方法有很多并发请求时,数据会损坏。我想,由于竞争条件,数据被错误地写入缓存。
最佳答案
您似乎遇到了某种临界区问题。但事情是这样的。 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
是一个 native redis 命令,如果它不存在则设置一个值。这是以原子方式完成的,因此它可用于实现简单的锁定机制,但是(如手册中所述)如果您使用的是 redis 集群,则将不起作用。在那种情况下,redis 手册描述了 a method to implement distributed locks
关于php - Laravel 缓存返回损坏的数据(redis 驱动程序),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46128080/