ThinkPHP 8.0 本身不内置负载均衡或跨节点读写分离能力,必须依赖 ProxySQL 等中间件实现;TP8 仅负责连接选择,ProxySQL 才承担真实流量分发、主从自动识别、故障剔除及事务一致性保障。

ThinkPHP 8.0 本身不内置负载均衡或跨节点读写分离能力,高可用集群需靠外部中间件(如 ProxySQL)+ 框架内配置协同实现。核心逻辑是:TP8 只负责“选连接”,真实流量分发、故障剔除、主从自动识别必须由 ProxySQL 完成;框架内配置仅控制“该用哪个连接”,不能替代中间件。
ProxySQL 是读写分离的必需中间件
TP8 不解析 SQL 语义,也不探测 MySQL 的 read_only 状态。它无法自动判断 SELECT 应发给哪台从库,更无法在某台从库宕机时跳过它。这些能力全部依赖 ProxySQL:
- TP8 数据库配置中的 hostname 必须指向 ProxySQL 的 IP 和端口(默认 6033),不能直连任何 MySQL 实例
- 用户名/密码填的是 ProxySQL
mysql_users表里定义的前端账号(如app_user),不是后端 MySQL 账号 - ProxySQL 通过扫描后端 MySQL 的
read_only值自动归类 hostgroup:主库进 group 0,从库进 group 1 - 所有
BEGIN、COMMIT、ROLLBACK相关 SQL 必须配置路由规则,强制走主库组,否则事务一致性无法保障
TP8 数据库配置必须启用 deploy + rw_separate
仅靠写多个 hostname 或改 DB_HOST 是无效的。TP8 要求显式开启部署模式,并严格遵循数组结构:
- deploy => 1 是总开关,缺了整个读写机制不初始化
- rw_separate => true 仅在 deploy=1 下生效,控制读操作是否尝试走从库
-
write 必须是单维数组(只允许一个主库地址),例如:
['hostname' => '192.168.1.10', 'port' => 3306] -
read 必须是二维数组(哪怕只有一个从库也要套一层),例如:
[['hostname' => '192.168.1.11'], ['hostname' => '192.168.1.12']] - 字符集(charset)、数据库名(database)、用户名密码等必须与主库完全一致,否则连接失败或乱码
事务、锁与强一致性场景要主动干预
TP8 的读写分离是“策略性”的,不是“语义级”的。以下情况会自动降级到主库:
立即学习“PHP免费学习笔记(深入)”;
- 当前处于事务中(
Db::transaction()或模型 save() 含事务块),所有查询包括select()都走主库 - 上一条执行的是写操作(INSERT/UPDATE/DELETE),后续
find()默认仍走主库,避免主从延迟导致查不到刚写入的数据 - 手动调用了
->master(true)或全局Db::master(),会锁定后续查询始终走主库 - 若业务要求“刚写完立刻能读”,不能依赖自动分离,应显式加
->useReadConnection(false)或直接走主库
多数据库和读写分离不能混用
这是两个完全独立的功能,配错会导致行为不可预期:
-
多数据库:用于不同业务库(如 user_db、log_db、order_db),通过
Db::connect('log_db')显式切换,每个连接需完整配置 type/host/database -
读写分离:针对同一个业务库的主从节点,只在一个连接(如
connections.mysql)内配置 write/read,不涉及connect()调用 - 模型设
$connection = 'log_db'不会触发读写分离;在mysql连接里配了 read/write,也不能用Db::connect('mysql_read')单独调用从库 - 关联查询(
with())不会跨库合并数据,MySQL 本身不支持跨实例 JOIN,TP8 也不会做数据聚合



















