必须开启app_multi开关,否则多应用数据库隔离失效;各应用需独立配置app/{name}/config/database.php;Db::connect()仅限当前应用连接池;跨库操作须手动查询合并。

必须先开 app_multi 开关,否则所有配置都白搭
多应用数据库隔离的前提是框架真正进入多应用流程。如果没在根目录 config/app.php 里设 'app_multi' => true,ThinkPHP 会始终走单应用逻辑——哪怕你建了 app/admin、写了 app/admin/config/database.php,框架也根本不会去读它,而是直接 fallback 到根目录的 config/database.php。
这个开关必须放在项目根目录的 config/app.php,不是某个子应用自己的 app/admin/config/app.php。设错位置或漏设,后果就是所有应用共享同一套数据库连接,完全谈不上隔离。
-
app_multi是总闸,不是可选项;不设它,后续所有「按应用配库」的动作全无效 - 别指望
APP_MULTI_MODULE常量能替代它——那个只影响路由解析,不控制配置加载路径 - 检查方式:临时删掉根目录
config/database.php,如果 admin 应用还能连上数据库,说明app_multi没生效,还在走单应用兜底逻辑
app/{name}/config/database.php 必须真实存在且独立
每个应用(如 admin、api)要实现数据库隔离,就得在自己目录下放一份完整的 database.php,路径是 app/admin/config/database.php。它和根目录的 config/database.php 完全无关,不继承、不合并、不覆盖。
常见错误是只改了根目录配置,以为子应用会自动同步;或者图省事,用软链接或 require 引入主配置——这些都会破坏隔离性。
立即学习“PHP免费学习笔记(深入)”;
- 哪怕两个应用配置一模一样,也得各自复制一份
database.php,不能省 - 文件必须存在,哪怕内容为空也会报
No database configuration found - 连接名(如
'mysql'、'log_db')可以重名,因为作用域仅限当前应用内部 - 确保
connections数组里每个连接都定义了'type'字段,否则会报Class 'PDO' not found
Db::connect() 只查当前应用的 connections
在 app/admin 里调用 Db::connect('log_db'),框架只会去 app/admin/config/database.php 的 connections 里找 log_db,绝不会跨到 app/api 或根目录去查。这是隔离的关键机制,也是很多人踩坑的地方。
如果你在 admin 控制器里写 Db::connect(['hostname' => '127.0.0.1']) 这种临时数组方式,它不走连接池,也不受 connections 约束,高并发下很快触发 Too many connections。
- 连接名禁止含点号(如
db.log)或大写字母(如LogDB),否则报Connection not found: xxx - 模型类里的
protected $connection = 'mysql_read'只对本应用生效;app/api/model/User.php必须单独设,否则默认走该应用自己的default连接 -
with()关联查询时,关联模型仍用它自己定义的$connection,不会自动跨库 join
跨应用数据操作只能手动查两次再 PHP 合并
框架不支持跨应用的数据库 join、事务合并或统一查询路由。比如 admin 要查用户信息(admin 库)并关联日志(log_db 库),不能写 User::with('logs') 期望自动跨库拉取——logs 关联模型若在 app/admin 下,它只会连 admin 库;若在 app/log 下,又得另起一个应用上下文。
实际做法只能是:先查出用户 ID 列表,再用这些 ID 去另一个库手动查日志,最后在 PHP 层合并。这看似笨重,但正是隔离设计的必然代价。
- 别试图用
Db::connect()在一个请求里来回切换连接做关联,容易丢失事务上下文或污染连接状态 - 如果真有高频跨库需求,建议把共用数据抽成微服务或中间表,而不是依赖框架层“假装能跨”
- 最易被忽略的是:模型类加载时就已锁定
$connection值,运行中 reload 配置不会刷新它——改了database.php得重启进程才生效



















