Laravel怎样避免缓存雪崩问题

来源:编程学习作者:梦乃头衔:网络博主
导读:本期聚焦于小伙伴创作的《Laravel怎样避免缓存雪崩问题》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《Laravel怎样避免缓存雪崩问题》有用,将其分享出去将是对创作者最好的鼓励。

在Laravel项目开发中,缓存雪崩是缓存使用过程中需要重点防范的问题。当大量缓存键在同一时间过期,或者缓存服务突然不可用,所有请求都会直接落到数据库上,导致数据库压力骤增甚至宕机,最终影响整个服务的可用性。下面介绍几种在Laravel中避免缓存雪崩的实用方法。

Laravel怎样避免缓存雪崩问题

设置随机过期时间

缓存雪崩最常见的诱因是大量缓存设置了相同的过期时间,过期时刻所有请求同时穿透到数据库。解决这个问题最简单的方法就是给缓存过期时间增加一个随机值,避免同时过期。

在Laravel中设置缓存时,可以通过Cache::put方法结合随机函数来生成过期时间,示例如下:

<?php
namespace AppServices;

use IlluminateSupportFacadesCache;

class UserService
{
    /**
     * 获取用户列表,带随机过期时间缓存
     */
    public function getUserList()
    {
        $cacheKey = 'user_list';
        // 基础过期时间30分钟,加上0-300秒的随机值,避免同时过期
        $baseTtl = 1800;
        $randomTtl = rand(0, 300);
        $ttl = $baseTtl + $randomTtl;

        // 尝试从缓存获取
        $data = Cache::get($cacheKey);
        if (!is_null($data)) {
            return $data;
        }

        // 缓存不存在,查询数据库
        $data = AppModelsUser::where('status', 1)->get()->toArray();
        // 存入缓存,设置随机过期时间
        Cache::put($cacheKey, $data, $ttl);

        return $data;
    }
}

使用互斥锁防止缓存击穿引发的雪崩

当某个热点缓存过期时,大量请求同时查询这个缓存,都会发现缓存不存在,然后同时去查询数据库,这种情况会加剧雪崩的风险。可以通过互斥锁的方式,让只有一个请求去查询数据库,其他请求等待缓存更新完成后再获取缓存。

Laravel中可以结合Redis的setnx命令实现简单的互斥锁,示例如下:

<?php
namespace AppServices;

use IlluminateSupportFacadesCache;
use IlluminateSupportFacadesRedis;

class ArticleService
{
    /**
     * 获取热点文章详情,带互斥锁
     */
    public function getHotArticle($articleId)
    {
        $cacheKey = 'hot_article_' . $articleId;
        $lockKey = 'lock_hot_article_' . $articleId;
        $lockExpire = 10; // 锁过期时间10秒

        // 先尝试从缓存获取
        $data = Cache::get($cacheKey);
        if (!is_null($data)) {
            return $data;
        }

        // 缓存不存在,尝试获取锁
        $lock = Redis::setnx($lockKey, 1);
        if ($lock) {
            // 获取锁成功,设置锁过期时间,防止死锁
            Redis::expire($lockKey, $lockExpire);
            // 查询数据库
            $data = AppModelsArticle::where('id', $articleId)->where('is_hot', 1)->first();
            if ($data) {
                $data = $data->toArray();
                // 存入缓存,设置随机过期时间
                $ttl = 3600 + rand(0, 600);
                Cache::put($cacheKey, $data, $ttl);
            }
            // 释放锁
            Redis::del($lockKey);
            return $data;
        } else {
            // 获取锁失败,等待100毫秒后重试
            usleep(100000);
            return $this->getHotArticle($articleId);
        }
    }
}

缓存预热与持久化策略

在系统启动或者缓存大量失效前,提前将热点数据加载到缓存中,也就是缓存预热,可以有效避免缓存同时失效的问题。另外,配置缓存的持久化策略,比如Redis开启RDB或者AOF持久化,即使缓存服务重启,也能快速恢复数据,减少雪崩的影响。

Laravel中可以通过命令行来实现缓存预热,示例如下:

<?php
namespace AppConsoleCommands;

use IlluminateConsoleCommand;
use IlluminateSupportFacadesCache;
use AppModelsArticle;

class CacheWarmupCommand extends Command
{
    protected $signature = 'cache:warmup';
    protected $description = '预热热点缓存数据';

    public function handle()
    {
        $this->info('开始预热缓存数据');
        // 预热热点文章缓存
        $hotArticles = Article::where('is_hot', 1)->limit(100)->get();
        foreach ($hotArticles as $article) {
            $cacheKey = 'hot_article_' . $article->id;
            $ttl = 3600 + rand(0, 600);
            Cache::put($cacheKey, $article->toArray(), $ttl);
        }
        $this->info('缓存预热完成');
    }
}

可以将这个命令配置到系统的定时任务中,或者在服务部署完成后手动执行,提前加载热点数据到缓存。

缓存降级与容错处理

当缓存服务完全不可用的时候,需要有降级策略,避免所有请求都打到数据库。可以在Laravel中配置缓存的容错逻辑,当缓存连接失败的时候,直接返回默认值或者空数据,同时记录日志,后续再排查缓存服务的问题。

可以通过自定义缓存驱动或者封装缓存调用的方式实现容错,示例如下:

<?php
namespace AppSupport;

use IlluminateSupportFacadesCache;
use IlluminateSupportFacadesLog;

class CacheHelper
{
    /**
     * 带容错的缓存获取方法
     * @param string $key 缓存键
     * @param mixed $default 缓存不存在或者失败时的默认值
     * @return mixed
     */
    public static function getWithFallback($key, $default = null)
    {
        try {
            $data = Cache::get($key);
            if (!is_null($data)) {
                return $data;
            }
            return $default;
        } catch (Exception $e) {
            // 缓存服务异常,记录日志,返回默认值
            Log::error('缓存获取失败:' . $e->getMessage(), ['key' => $key]);
            return $default;
        }
    }

    /**
     * 带容错的缓存存储方法
     * @param string $key 缓存键
     * @param mixed $value 缓存值
     * @param int $ttl 过期时间(秒)
     * @return bool
     */
    public static function putWithFallback($key, $value, $ttl = 3600)
    {
        try {
            return Cache::put($key, $value, $ttl);
        } catch (Exception $e) {
            Log::error('缓存存储失败:' . $e->getMessage(), ['key' => $key]);
            return false;
        }
    }
}

在业务代码中调用这个辅助类来获取和存储缓存,就可以在缓存服务异常的时候自动降级,避免请求直接穿透到数据库。

多级缓存架构

除了单级缓存,还可以使用多级缓存的架构,比如本地缓存加分布式缓存的组合。本地缓存可以使用Laravel的array缓存驱动,分布式缓存使用Redis。请求先查本地缓存,再查分布式缓存,最后才查数据库。即使分布式缓存出现问题,本地缓存还可以扛住一部分请求,减少雪崩的影响。

多级缓存的简单实现示例如下:

<?php
namespace AppServices;

use IlluminateSupportFacadesCache;

class ConfigService
{
    /**
     * 获取系统配置,多级缓存
     */
    public function getConfig($key)
    {
        // 先查本地缓存(array驱动,请求生命周期内有效)
        $localKey = 'local_config_' . $key;
        $localCache = Cache::store('array')->get($localKey);
        if (!is_null($localCache)) {
            return $localCache;
        }

        // 再查分布式缓存(Redis)
        $redisKey = 'config_' . $key;
        $redisCache = Cache::store('redis')->get($redisKey);
        if (!is_null($redisCache)) {
            // 存入本地缓存,避免重复查分布式缓存
            Cache::store('array')->put($localKey, $redisCache, 60);
            return $redisCache;
        }

        // 缓存都不存在,查数据库
        $config = AppModelsConfig::where('key', $key)->first();
        if ($config) {
            $value = $config->value;
            // 存入分布式缓存,设置随机过期时间
            $ttl = 86400 + rand(0, 3600);
            Cache::store('redis')->put($redisKey, $value, $ttl);
            // 存入本地缓存
            Cache::store('array')->put($localKey, $value, 60);
            return $value;
        }

        return null;
    }
}

通过以上几种方法的组合使用,可以有效避免Laravel项目中出现缓存雪崩的问题,提升系统的稳定性和可用性。在实际项目中,可以根据业务场景选择合适的方案,或者将多种方案结合使用,达到更好的防护效果。

Laravel缓存雪崩缓存容错Redis修改时间:2026-06-14 22:27:24

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。