提取10个项目的缓存Eloquent集合超时

时间:2018-12-30 20:24:52

标签: laravel redis

我正在构建一个搜索功能,该功能返回使用LengthAwarePaginator分页的大型集合。我正在尝试使用名为$ searchFilter_ $ query_ $ offsetPages的键为返回结果的单个页面(10个项目)缓存结果。它进入缓存就好了。但是,当我尝试使用Cache :: has($ key)进行检查或使用Cache :: get($ key)进行获取时,超时。

在浏览器以及工匠Tinker中也会发生相同的问题。奇怪的是,当我将随机的10个项目集放入Tinker的缓存中并取回它们时,一切正常。我正在使用Redis作为缓存驱动程序。

这是我的控制器方法:

public function search($filter, $query, $layout, Request $request) {
  if($layout == "list-map") {
    return view("list-map")->with(['filter' => $filter, 'query' => $query, 'layout' => 'list-map']);      
  } else {
    $offsetPages = $request->input('page', 1) - 1;
    $cacheKey = $filter . "_" . $query . "_" . $offsetPages;

    if(Cache::has($cacheKey)) {
      \Log::info("fetching results from cache");
      $data = Cache::get($cacheKey);

      $totalCt = $data[0];
      $results = $data[1];
    } else {
      $results = $this->getResults($filter, $query);
      $totalCt = $results->count();
      $results = $results->slice($offsetPages, $this->resultsPerPage);

      \Log::info("caching results");
      Cache::put($cacheKey, [$totalCt, $results], 5);
    }

  $results = new LengthAwarePaginator($results,
    $totalCt,
    $this->resultsPerPage,
    $request->input('page', 1),
    ['path' => LengthAwarePaginator::resolveCurrentPath()]
  );

    return view($layout)->with(['filter' => $filter, 'query' => $query, 'layout' => $layout, 'results' => $results]);
  }

}

2 个答案:

答案 0 :(得分:1)

因此,问题在于,我的getResults()方法返回的集合中的许多模型都是通过关系查询获得的。当我在10个结果的单页上dd($ results)时,我可以看到每个模型上都有一个“关系”字段。在该数组中,有数千个基于我最初查询的关系的递归相关模型。我找不到有关不希望加载这些相关模型的选项的任何信息。相反,我想出了一些小技巧来直接获取模型:

  $results = $results->slice($offsetPages, $this->resultsPerPage);

      //load models directly so they don't include related models.
      $temp = new \Illuminate\Database\Eloquent\Collection;
      foreach($results as $result) {
        if(get_class($result) == "App\Doctor") {
          $result = Doctor::find($result->id);
        } else if(get_class($result == "App\Organization")) {
          $result = Organization::find($result->id);
        }
        $temp->push($result);
      }
      $results = $temp;

      \Log::info("caching results");
      Cache::put($cacheKey, [$totalCt, $results], 5);

如果有人知道这种情况下的最佳做法,请告诉我。谢谢!

编辑: 我找到了一个更好的解决方案,而不是上述解决方法。如果我这样查询我的关系:$ taxonomy-> doctors()-> get()而不是$ taxonomy-> doctors,则它不会加载到巨大的可追溯关系中。

答案 1 :(得分:0)

我真的不明白为什么您的代码不起作用。我看到的唯一潜在问题是缓存键,其中可能包含有问题的字符,以及检查缓存值的方式。在Cache::has($key)之前使用Cache::get($key)时,您可能会遇到竞争状况,其中第一个调用返回true,而后一个null因为缓存值在这两个电话。

我尝试在以下代码段中解决这两个问题:

public function search($filter, $query, $layout, Request $request)
{
    if($layout == "list-map") {
        return view("list-map")->with(['filter' => $filter, 'query' => $query, 'layout' => 'list-map']);      
    } else {
        $offsetPages = $request->input('page', 1) - 1;
        $cacheKey = md5("{$filter}_{$query}_{$offsetPages}");
        $duration = 5; // todo: make this configurable or a constant

        [$totalCount, $results] = Cache::remember($cacheKey, $duration, function () use ($filter, $query) {
            $results = $this->getResults($filter, $query);
            $totalCount = $results->count();
            $filteredResults = $results->slice($offsetPages, $this->resultsPerPage);

            return [$totalCount, $filteredResults];
        });

        $results = new LengthAwarePaginator($results,
            $totalCount,
            $this->resultsPerPage,
            $request->input('page', 1),
            ['path' => LengthAwarePaginator::resolveCurrentPath()]
        );

        return view($layout)->with(compact('filter', 'query', 'layout', 'results'));
    }
}

内置函数Cache::remember()在后​​台不使用Cache::has()。相反,它将仅调用Cache::get()。由于如果未命中任何缓存,此函数将默认返回null,因此该函数可以轻松确定是否必须执行关闭操作。

我还将$cacheKey包裹在md5()中,这给出了一个始终有效的密钥。


查看代码的以下部分

$results = $this->getResults($filter, $query);
$totalCount = $results->count();
$filteredResults = $results->slice($offsetPages, $this->resultsPerPage);

我很确定可以改善整个搜索(独立于缓存)。因为似乎您将特定搜索的所有结果都加载到内存中,即使您丢弃大部分内容也是如此。当然,有更好的方法可以做到这一点。