运行时切换数据库连接必须使用DB::connection()获取独立连接实例,而非修改配置;其与模型$connection属性本质不同,前者临时一次性,后者静态绑定,且跨库事务不被支持。

运行时切换 DB 连接用 DB::connection(),不是改配置文件
改 config/database.php 或环境变量只能影响启动后的默认连接,运行时切换必须靠代码显式指定连接实例。Laravel 的 DB::connection() 返回一个独立的数据库连接对象,它和默认连接完全隔离——事务、查询日志、连接状态互不影响。
常见错误是以为调用 Config::set('database.default', 'mysql2') 就能切库,其实这只会改配置缓存,对已创建的 DB 实例无效,新请求才可能生效。
- 必须用
DB::connection('mysql2')获取目标连接实例,再在其上调用table()、select()等方法 - 连接名(如
'mysql2')需提前在config/database.php的'connections'数组里定义好 - 不推荐在模型里硬编码连接名,容易耦合;更适合在 Service 类或 Repository 中按业务逻辑传入连接名
DB::connection() 和模型 $connection 属性的区别
模型上的 $connection = 'pgsql' 是静态绑定,整个模型生命周期都走这个连接,适合「长期归属某库」的场景(比如日志表固定在 logs 库)。而 DB::connection() 是临时、一次性的,适合「同一请求中查多个库」的场景(比如订单查 orders,用户查 users)。
容易踩的坑:混用两者导致行为不一致。例如模型设了 $connection = 'mysql2',又手动调用 DB::connection('mysql1')->table('users')->get(),结果两个查询走的库不同,但开发者误以为都在操作同一套数据。
- 模型级切换只改
$connection属性,不触发重连,前提是该连接名已配置且可用 -
DB::connection()每次调用都会检查连接是否存在,不存在则新建,有连接池复用机制 - 若用 Eloquent 查询构造器(如
DB::connection('foo')->table('bar')->where(...)),它不走模型事件、访问器、强制作用域等,纯 SQL 层操作
事务跨库会失败,别指望 DB::transaction() 包住两个连接
Laravel 的 DB::transaction() 只针对单个连接实例。如果你写:
DB::transaction(function () {
DB::connection('mysql1')->table('orders')->insert([...]);
DB::connection('mysql2')->table('logs')->insert([...]);
});这段代码看似包在事务里,实际只有第一个查询受事务控制,第二个走的是另一条连接,提交/回滚互不感知——MySQL 本身也不支持跨库事务(除非用 XA,但 Laravel 不原生支持)。
- 需要强一致性时,得用应用层补偿:先写主库,成功后再写日志库;失败则发消息重试或记录待处理队列
- 如果只是想「确保本库操作原子性」,那就分别对每个连接单独开事务:
DB::connection('mysql1')->transaction(...)和DB::connection('mysql2')->transaction(...) - 注意连接复用:同一个连接实例在事务中多次调用
DB::connection('name')返回的是同一个对象,事务状态延续;但不同连接名之间完全独立
连接名拼错、配置缺失时的报错很隐晦,优先查 ConnectionNotFoundException
典型错误信息是:Illuminate\Database\ConnectionNotFoundException: Database connection [xxx] not configured. 表示你在 DB::connection('xxx') 里写的连接名,在 config/database.php 的 'connections' 数组里根本不存在。但很多人卡在「明明写了,为啥找不到」,问题往往出在配置加载时机或拼写细节。
- 检查是否漏掉
php artisan config:clear—— 修改配置后不清理缓存,config:database.php可能还是旧的 - 连接名区分大小写,
'mysql_slave'和'mysql-slave'是两个不同连接 - 多环境时确认当前环境(
APP_ENV)下该连接是否被条件性定义,比如只在.env.local里配了DB_CONNECTION_SECONDARY,但生产环境没配 - 用
DB::getConnections()在 tinker 里直接看当前已注册的连接名列表,比翻配置更快
动态切库不是黑魔法,核心就两条:连接名得存在、调用得用对实例。真正难的从来不是怎么切,而是切完之后怎么管好事务边界、连接生命周期和错误兜底——这些地方一松懈,线上就容易出现数据不一致或连接耗尽。


















