Laravel缓存返回损坏的数据(redis驱动程序)

时间:2017-09-09 07:08:32

标签: php laravel caching redis

我有一个用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获取数据,处理并放置到缓存中,然后返回。

问题是:当方法有很多并发请求时,数据已损坏。我想,由于竞争条件,数据被写入缓存不正确。

2 个答案:

答案 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秒。