laravel 默认不支持 mongodb 缓存驱动,因其原生缓存系统仅内置 file、database、redis、memcached 等驱动;所谓“支持”实为依赖第三方包 mongodb/laravel-mongodb,需手动安装、配置、建 ttl 索引并注意序列化与锁安全问题。

为什么 Laravel 默认不支持 MongoDB 缓存驱动
Laravel 原生缓存系统只内置了 file、database、redis、memcached 等驱动,mongodb 不在其中。你看到的“Laravel 支持 MongoDB 缓存”,实际依赖的是第三方包 mongodb/laravel-mongodb(原 jenssegers/mongodb),它在内部实现了 MongoStore 类并注册为可选驱动。没装这个包就直接配 'driver' => 'mongodb',启动会报错:Driver [mongodb] not supported。
安装与注册 mongodb 缓存驱动的最小必要步骤
跳过冗余依赖和旧版兼容性处理,只做能跑通缓存写入/读取的四件事:
- 用 Composer 安装最新稳定版:
composer require mongodb/laravel-mongodb(确认兼容 Laravel 10.x+) - 发布配置(如果 vendor 发布命令可用):
php artisan vendor:publish --provider="MongoDB\Laravel\MongoServiceProvider" - 在
config/cache.php的'stores'中添加配置块,**必须显式指定lock_collection**,否则Cache::lock()会失败:
'mongodb' => [
'driver' => 'mongodb',
'connection' => 'mongodb', // 对应 config/database.php 中的 mongodb 连接名
'collection' => 'cache',
'lock_collection' => 'cache_locks',
],
- 把
'default'改成'mongodb'或在环境变量中设CACHE_DRIVER=mongodb
缓存项结构和 TTL 索引必须手动创建
MongoDB 不会自动清理过期缓存,靠的是 TTL 索引。但 MongoStore 的 createTTLIndex() 方法不会在每次请求时调用——它只在你显式执行时才建索引。漏掉这步,put($key, $value, $seconds) 写进去的数据永远不自动过期。
- 首次部署后,运行 Artisan 命令一次性创建:
php artisan tinker→(new \MongoDB\Laravel\Cache\MongoStore(app('mongodb'), 'cache', 'cache_locks'))->createTTLIndex() - 或者在迁移中调用(推荐):
\MongoDB\Laravel\Cache\MongoStore::createTTLIndex()需传入 collection 实例,注意连接上下文 - 验证是否生效:
db.cache.getIndexes()应看到类似{ "key": { "expires_at": 1 }, "expireAfterSeconds": 0 }
不建索引的后果不是报错,而是缓存无限堆积,磁盘缓慢增长,且 Cache::get() 仍能读到已逻辑过期的数据(因为 get() 内部有运行时检查,但 flush() 或后台扫描无法触发清理)。
常见踩坑:序列化、锁冲突与并发安全
MongoStore 默认用 PHP serialize() 存值,如果你缓存的对象含闭包、资源或 Laravel 的未序列化模型实例,put() 会静默失败或读取时反序列化报错。
- 避免缓存 Eloquent 模型,改用数组:
$user->toArray()或$user->only(['id', 'name']) -
Cache::lock('foo')->get()在高并发下可能返回null—— 因为lock_collection没设唯一索引,多个进程可能同时写入同名锁文档;需手动确保cache_locks._id是唯一键 - 批量预热慎用
Cache::putMany():当前mongodb/laravel-mongodbv5.x 尚未实现该方法,会 fallback 到逐条put(),性能差;改用原生 MongoDB 批量操作 + TTL 字段写入更稳
最易被忽略的一点:MongoDB 缓存没有「内存穿透保护」。当大量请求同时发现缓存 miss 并回源查询,MongoStore 不提供类似 Redis 的原子 set-if-not-exists 语义,所有请求都会走数据库查原始数据——你要自己加 Cache::lock() 包裹回源逻辑。











