laravel中cache必须用singleton()绑定或直接使用cache门面,因bind()每次make都新建redis连接导致连接池耗尽;正确做法是在appserviceprovider@register()中singleton注册,或通过cache::store()复用已缓存的底层连接。

Cache 不能靠容器 bind() 注入,必须用 singleton() 或直接走 Cache facade —— 否则每次请求新建 Redis 连接,5 分钟内打爆连接池。
为什么 bind('cache', ...) 会出事
bind() 是“每次 make 都 new 一次”的工厂模式。而 Cache 底层的 Repository 和它依赖的 Store(比如 RedisStore)都有状态:连接句柄、序列化器、前缀配置等。用 bind() 注册会导致:
- 每次控制器方法或命令执行时,
app()->make('cache')都新建一个Repository实例,又新建一个RedisStore,又新建一个Predis\Client连接 - 连接不复用,Laravel 默认不主动 close,PHP-FPM worker 生命周期内连接持续堆积
- Redis 报错
ERR max number of clients reached,或 Laravel 抛Connection refused - 即使你写了
$this->app->bind(CacheContract::class, ...),类型提示注入也会触发相同行为
正确绑定方式:只用 singleton() 或跳过绑定
真正安全的做法只有两种,没有第三种:
- 在
AppServiceProvider@register()中写:$this->app->singleton('cache', function ($app) { return $app->make(\Illuminate\Cache\CacheManager::class)->store(); });—— 注意:这里不是 new Repository,而是让CacheManager按当前配置(CACHE_DRIVER=redis)返回 store 实例 - 更推荐直接放弃手动绑定,改用
Cache::facade:它内部已通过singleton()绑定了cache键,且自动处理 store 切换和连接复用 - 若需多 store(如同时用 redis + file),不要 bind 多个 cache,而是用
Cache::store('redis')或Cache::store('file')显式调用
Cache::store('redis') 背后到底发生了什么
当你调用 Cache::store('redis'),Laravel 并不是每次都 new 一个新客户端。它实际做了三件事:
- 从
CacheManager的$stores缓存数组中查找是否已有redis实例;有则直接返回 - 没有时,才根据
config('cache.stores.redis')创建RedisStore,而这个RedisStore内部的Predis\Client或PhpRedis连接,是通过singleton()注册在容器里的(见RedisServiceProvider) - 所以真正被复用的是底层连接,不是 facade 本身
这也是为什么你不能在 boot() 里写 $this->app->singleton('cache'):CacheManager 在 register() 阶段已初始化完毕,boot() 时再绑定,Cache:: 已经用上了旧实例。
最容易被忽略的硬性前提
无论你怎么绑定,以下三点没做对,Cache 就永远走不了 Redis:
-
CACHE_DRIVER=redis必须写在.env,且执行过php artisan config:clear && php artisan config:cache -
config/cache.php中'default' => env('CACHE_DRIVER', 'file')不能是file,否则Cache::get()默认不进 Redis;要么改 default,要么坚持用Cache::store('redis')->get() -
config/database.php的redis.connections.cache(或默认default)必须能连通,用php artisan tinker --execute="resolve('redis.connection')->ping()"可单独验证











