ThinkPHP多数据库查询慢的主因是查询逻辑未适配多库场景,包括主从配置失效、跨库with()引发N+1、事务跨连接无效、字典表重复查询等;优化关键在于让查询精准路由到目标库并消除协调开销。

ThinkPHP 多数据库查询慢,90% 不是“跨库”本身的问题,而是查询逻辑没对齐多库场景:主从读写分离配置错、跨库 JOIN 强行用 with()、事务横跨多个连接、或每个库都重复查相同字典表。优化核心不是换框架,而是让每个查询落在它该在的库上,且不额外增加协调成本。
主从配置没生效,所有读请求全打到主库
现象是 Db::name('user')->select() 明明配了读写分离,但 MySQL 主库 CPU 持续 90%+,从库流量几乎为零。
- 检查
config/database.php中'read' => ['host' => [...]]是否真实填了从库地址,而非留空或写成['host' => '127.0.0.1'] -
'master_balance' => true必须开启,否则只用第一个从库;若只配了一个从库,该选项无效 - 确认没在查询链路里手动调用
Db::connect('mysql')或Db::name('user')->connect('mysql'),这会绕过读写分离自动路由 - 用
Db::getLastSql()+Db::getRealSql()对比,看实际执行的 SQL 是发给了哪个连接(连接名会打印在日志里)
跨库关联用 with() 导致 N+1 翻倍恶化
比如用户在 db_main,订单在 db_trade,写 UserModel::with('orders')->select(),框架会先查用户,再为每个用户单独连 db_trade 查订单——等于开了 N 个独立数据库连接,网络和连接池压力爆炸。
- 跨库不能用
with(),必须手动拆:先查UserModel,拿到$userIds后,显式切库查订单:Db::connect('db_trade')->name('order')->where('user_id', 'in', $userIds)->select() - 如果要字段对齐,PHP 层用
collection($users)->mapWithKeys(...)做内存级关联,别把压力甩给数据库 - 绝对避免
whereHas()跨库,它生成的子查询无法跨连接执行,TP 会静默降级为 PHP 循环匹配,数据一多直接 OOM
事务里混用多个数据库连接
代码里写了 Db::startTrans(),然后既操作 user 表又操作 log 表(后者在另一库),结果事务根本没生效,还卡住连接池。
- MySQL 原生不支持跨库事务(XA 事务 TP 不默认支持,且极难维护),TP 的
startTrans()只对当前连接有效 - 要么放弃跨库事务,用最终一致性(如发 MQ 补单);要么把相关表挪到同一库,这是最省心的解法
- 若真要用 XA,得手动调 PDO 的
beginTransaction()+prepare('XA START...'),TP 的 Db 类完全不封装这一层
字典表重复查询,每个库都跑一遍 SELECT *
比如 region(省市区)表在每个业务库都有一份,但代码里每次查用户都 RegionModel::select(),导致 5 个库各查一次,IO 和网络翻 5 倍。
立即学习“PHP免费学习笔记(深入)”;
- 字典类数据必须抽离为独立缓存服务,用 Redis 存,键名如
dict:region,过期时间设长(如 24 小时) - 禁止在模型的
initialize()或getAttr()里触发跨库查询,这类钩子极易被忽略,变成隐藏性能杀手 - 如果非得查库,统一走一个只读连接(比如
db_dict),而不是让每个业务模型自己连自己的库去查
Db::getConnectInfo() 打印每次查询走的是哪个连接,再对照日志看有没有意外的连接重建或跨库跳转——这才是第一排查点。



















