thinkphp多数据库与读写分离是两种不同机制:多数据库通过db::connect('name')切换预定义连接,连接名须为合法php变量名且配置完整;读写分离需在default连接中启用deploy=>1、rw_separate=>true,并严格按数组套数组格式配置read/write。

ThinkPHP 多数据库和读写分离不是一回事,配错一个键名,读库就永远不生效——必须分清“多个业务库”和“同一库的主从节点”两种场景。
Db::connect('xxx') 只认 config/database.php 里的 connections 键名
你不能在代码里临时传数组:Db::connect(['hostname' => '192.168.1.11']),这会新建连接、不复用、高并发下触发 Too many connections。也不能用带点号或大写字母的连接名,比如 'log.db' 或 'LogDB',Db::connect() 会直接抛出 Connection not found: log.db。
正确做法是:在 config/database.php 的 connections 数组里预定义好完整配置,例如:
return [
'default' => 'mysql',
'connections' => [
'mysql' => [
'type' => 'mysql',
'hostname' => '192.168.1.10',
'database' => 'main_db',
'username' => 'app_rw',
'password' => 'xxx',
'charset' => 'utf8mb4',
],
'log_db' => [
'type' => 'mysql',
'hostname' => '192.168.1.11',
'database' => 'log_db',
'username' => 'app_ro',
'password' => 'yyy',
'charset' => 'utf8mb4',
],
],
];
- 所有连接名必须是合法 PHP 变量名(小写字母 + 下划线 + 数字,不能以数字开头)
- 漏写
'type' => 'mysql',框架可能用默认驱动去连 PostgreSQL,报错Class 'PDO' not found - 复制粘贴后只改了
database,忘了改hostname,两个连接实际连向同一台 MySQL 实例,等于白配
读写分离必须同时满足 deploy => 1、rw_separate => true、read 是数组套数组
只写 'read' => ['192.168.1.11'] 是无效的,TP6 要求 read 必须是「数组套数组」,哪怕只有一个从库也要写成 'read' => [['hostname' => '192.168.1.11']];write 则只能是单个数组,写成数组套数组会直接报错 Invalid write database config。
关键开关缺一不可:
- 漏掉
'deploy' => 1,整个读写分离模块根本不会初始化 -
'rw_separate' => false或没写,所有查询默认走主库 -
default指向的连接名(如'mysql')必须在connections中存在且含上述配置
示例片段(放在 connections.mysql 内):
'deploy' => 1,
'rw_separate' => true,
'read' => [
['hostname' => '192.168.1.11', 'database' => 'main_db'],
['hostname' => '192.168.1.12', 'database' => 'main_db'],
],
'write' => ['hostname' => '192.168.1.10', 'database' => 'main_db'],
Db::name() 走读写分离,Db::connect() 绕过读写分离
Db::name('user')->select() 是否走从库,完全取决于它背后那个默认连接(即 connections.mysql)是否启用了 rw_separate;而 Db::connect('log_db') 是全新连接实例,只连自己配置里的地址,跟 read/write 完全无关。
也就是说:
- 想用读写分离 → 必须用
Db::name()或Db::table(),并确保默认连接已正确配置 - 临时查日志库 → 用
Db::connect('log_db'),但它不参与任何读写路由 - 事务中所有查询(哪怕只是
find())强制走主库,->slave()会被忽略 - 带锁查询(
->lock(true)或原生 SQL 含FOR UPDATE)也强制走主库
关联查询 with() 不自动继承主模型的读库策略
User::with('profile')->select() 中,User 模型走从库,Profile 关联模型仍按自身配置走主库——MySQL 本身不支持跨实例 JOIN,TP 也不会合并数据。
要让关联也走从库,必须显式指定:
- 在关联定义里加
->connection('slave'),例如:hasOne('Profile')->connection('slave') - 或者统一在
connections中定义'slave'连接名,所有关联都指向它 -
withCount()、withAvg()等聚合关联同样适用该规则
别指望加个 ->slave() 就全局生效,模型层不参与路由决策,连接层才管这事。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











