【问题标题】:Laravel cache returns corrupt data (redis driver)Laravel 缓存返回损坏的数据(redis 驱动)
【发布时间】:2018-02-18 01:33:36
【问题描述】:

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

问题是:当方法有很多并发请求时,数据被破坏了。我想,由于竞争条件,数据被错误地写入缓存。

【问题讨论】:

  • 旁注,您可以使用$value = Cache::remember(key, duration, callback) 从缓存中获取值(如果存在)或获取回调结果(如果不存在)。这也可以确保原子性(尽管不确定)。但是redis并没有真正从并发设置请求中中断,这很好,问题一定是并发远程请求
  • 好的,我试试
  • 使用什么缓存驱动?
  • redis 缓存驱动
  • @apokryfos,不幸的是 $value = Cache::remember(key, duration, callback) 不起作用。

标签: php laravel caching redis


【解决方案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

【讨论】:

  • 我会试一试并回复你!
【解决方案2】:

最后我得出了以下解决方案:我使用 Laravel 5.5 助手中的 retry() 函数来获取缓存值,直到它以 1 秒的间隔正常写入那里。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-06-15
    • 1970-01-01
    • 2019-10-29
    • 2018-01-30
    • 2021-07-22
    • 1970-01-01
    相关资源
    最近更新 更多