Laravel 5.5+Redis多自定义过滤器场景下缓存键设计优化问询
解决多过滤器场景下的Laravel缓存键设计问题
这确实是个非常常见的痛点——当过滤器数量从2个扩展到10个甚至更多时,手动拼接请求参数到缓存键里不仅容易遗漏、出错,还会让键变得冗长不堪,维护起来特别麻烦。我来分享几个实用且可扩展的解决方案:
1. 用过滤参数的哈希值生成缓存键标识
核心思路是:把所有和过滤相关的请求参数提取出来,排序后生成一个固定长度的哈希值,用这个哈希值代替零散的参数拼接。这样不管有多少个过滤器,缓存键的长度都保持一致,且能保证相同过滤条件生成唯一的键。
示例代码:
// 第一步:明确指定需要纳入缓存标识的过滤参数(避免混入分页、排序等无关参数) $filterParams = request()->only([ 'price_from', 'price_to', 'brand_id', 'color', 'is_in_stock' // 后续新增的过滤器参数直接在这里添加即可 ]); // 第二步:对参数按键排序,避免参数顺序不同导致哈希不一致(比如?price_from=10&price_to=100和?price_to=100&price_from=10应该是同一个缓存) ksort($filterParams); // 第三步:将参数数组转为JSON后生成哈希值(md5或sha1都可以,md5长度更短) $filterHash = md5(json_encode($filterParams)); // 第四步:构建简洁的缓存键 $cacheKey = sprintf('category_%s_page_%s_filters_%s', $category->alias, $page, $filterHash); // 正常使用缓存逻辑 $products = Cache::remember($cacheKey, 1440, function() use ($childrenCategoriesIndexes){ return Product::whereIn('category_id', $childrenCategoriesIndexes) ->userFilter() ->paginate(15); });
2. 封装过滤条件为结构化数组(更灵活的方案)
如果你的过滤器逻辑不仅仅是直接读取请求参数(比如有默认值、参数转换、或者部分条件来自其他来源),可以把过滤条件的生成逻辑封装成一个独立方法,返回结构化的条件数组,再基于这个数组生成哈希。
比如在Product模型中新增方法:
public static function getFilterConditions() { $conditions = []; // 价格区间过滤 if ($priceFrom = request('price_from')) { $conditions['price_min'] = (float)$priceFrom; } if ($priceTo = request('price_to')) { $conditions['price_max'] = (float)$priceTo; } // 品牌过滤 if ($brandId = request('brand_id')) { $conditions['brand_id'] = (int)$brandId; } // 库存状态过滤 $conditions['in_stock'] = request('is_in_stock', false) ? true : false; // 后续新增过滤器直接在这里添加即可 return $conditions; }
然后生成缓存键的逻辑就变成:
$filterConditions = Product::getFilterConditions(); ksort($filterConditions); $filterHash = md5(json_encode($filterConditions)); $cacheKey = sprintf('category_%s_page_%s_filters_%s', $category->alias, $page, $filterHash);
这种方式的好处是:过滤逻辑和缓存键生成逻辑解耦,后续修改过滤器规则时,不需要同时修改缓存键的拼接代码,更易维护。
3. 配合缓存标签实现批量清除(可选优化)
如果需要在分类数据更新时,批量清除该分类下所有带过滤器的缓存,可以给缓存加上标签:
$products = Cache::tags(['category_'.$category->alias])->remember($cacheKey, 1440, function() use ($childrenCategoriesIndexes){ return Product::whereIn('category_id', $childrenCategoriesIndexes) ->userFilter() ->paginate(15); });
后续当分类下的商品更新时,只需执行一行代码就能清除该分类下所有相关缓存:
Cache::tags(['category_'.$category->alias])->flush();
额外提醒
- 别忘了把分页相关的参数(比如
per_page如果允许自定义的话)也纳入缓存键,避免不同分页条数的缓存冲突。 - 如果你的过滤器中有数组类型的参数(比如多选的分类ID),
json_encode也能正常处理,排序后生成的哈希依然可靠。
内容的提问来源于stack exchange,提问作者Alexxosipov
相关产品推荐
相关产品推荐

