Laravel怎么配置多数据库连接_Laravel多DB切换教程【复杂】

3次阅读

在 config/database.php 的 connections 数组中添加 mysql_secondary 键,结构同 mysql 但配置独立 host、database 等参数,注意 prefix、charset(推荐统一 utf8mb4)及 strict/engine 兼容性;model 需显式设 protected $connection = ‘mysql_secondary’ 或用 ::on() 切换;迁移须用 –database=mysql_secondary;db::raw() 不绑定连接,执行库由外层查询决定;事务、observer、子查询均不自动继承连接,需手动指定。

Laravel怎么配置多数据库连接_Laravel多DB切换教程【复杂】

config/database.php 里怎么加第二个 MySQL 连接

直接在 connections 数组里追加一个键名(比如 mysql_secondary),结构和默认的 mysql 一致,但填上另一套 host、database、username 等。别漏掉 prefixcharset —— 如果主库用了 utf8mb4 而新库是 utf8,查中文可能乱码或报错 SQLSTATE[HY000]: General Error: 1366 Incorrect String value

常见错误:复制了整个 mysql 配置块却没改 hostdatabase,结果两个连接指向同一库,切来切去还是查的同一个地方。

  • 键名必须全小写、无下划线以外的符号(laravel 的 DB facade 内部用字符串匹配,mysql2 可以,mysql-2 会报 InvalidArgumentException: Database [mysql-2] not configured
  • 如果新库用的是不同版本 MySQL(比如 5.7 vs 8.0),记得检查 strictengine 设置是否兼容
  • read/write 分离配置不要和多连接混着写——那是另一套机制,强行叠在一起会导致 DB::connection('xxx')->table('y')->get() 偶尔走错连接

DB::connection(‘xxx’) 切换后为什么 Model 还走默认库

Model 默认绑定的是 protected $connection = 'mysql',手动调用 DB::connection('mysql_secondary') 只影响该次查询,对 Eloquent 没用。想让某个 Model 固定走副库,得显式指定:

class Report extends Model {     protected $connection = 'mysql_secondary'; }

容易踩的坑:在控制器里写了 DB::connection('mysql_secondary')->table('reports')->get(),顺手又写了 Report::all(),后者仍走 mysql —— 两个查询实际连了不同库,数据对不上还查不出问题。

  • 临时切换 Model 的连接,可以用 Report::on('mysql_secondary')->get(),比改 $connection 更安全
  • 如果 Model 有 Observer 或 Accessor 里调了其他 Model,那些关联 Model 不会自动继承当前连接,得各自指定
  • 使用 DB::transaction() 时,所有语句都必须在同一连接上,跨连接事务不生效,也不会报错,只会在副库提交、主库回滚,造成数据不一致

迁移文件怎么指定写到哪个数据库

运行 php artisan migrate --database=mysql_secondary,关键在 --database 参数值必须和 config/database.phpconnections 的键名完全一致。漏掉这个参数,哪怕 Model 指定了 $connection,迁移也只会跑在默认库上。

注意:迁移文件本身不感知连接,它只是 SQL 模板;真正决定执行位置的是命令行参数database.php 中的 default 值。

  • 多库共用一套迁移?危险。比如 create_users_table 在主库建了,在副库再跑一次会报 SQLSTATE[42S01]: Base table or view already exists,但 Laravel 默认忽略这个错误,看起来“成功”了,其实副库表结构可能没更新
  • 想让某些迁移只在特定库执行,得在 up() 里加判断:if ($this->connection === 'mysql_secondary') { ... },但更稳妥的做法是拆成两套 migration 目录,用 --path 分开跑

DB::raw() 和查询构造器在多连接下要注意什么

DB::raw() 本身不绑定连接,它只是生成一段原生 SQL 字符串。真正决定执行位置的是它被挂在哪条查询链上。比如:

DB::connection('mysql_secondary')   ->table('orders')   ->select(DB::raw('COUNT(*) as cnt'))   ->get(); // ✅ 走副库
DB::table('orders')   ->select(DB::raw('COUNT(*) as cnt'))   ->get(); // ❌ 走默认库,DB::raw 不改变连接

性能影响:如果副库是只读从库,但你在 DB::raw() 里写了 INSERTUPDATE,Laravel 不会拦截,MySQL 直接报错 SQLSTATE[HY000]: General error: 1290 The MySQL server is running with the --read-only option

  • DB::statement()DB::insert() 等写操作,必须显式指定连接,否则永远走默认库
  • 子查询里嵌套 DB::raw() 时,外层连接决定了执行库,内层不会“自动切换”
  • 如果副库字段类型和主库不一致(比如主库 json 字段,副库是 text),DB::raw('JSON_EXTRACT(...)') 在副库会直接报错,而不是静默返回 NULL

复杂点在于连接不是全局开关,而是每个查询实例的属性;容易被忽略的是迁移、事务、Observer 这三处,它们表面看和连接无关,实则每一步都依赖连接上下文是否被正确传递。

text=ZqhQzanResources