ThinkPHP读写分离需手动配置主从连接并启用deploy和rw_separate,写操作强制走主库,读操作默认轮询从库,事务内全部走主库。

ThinkPHP 的读写分离不是开箱即用的“自动切换”,它依赖于你显式配置主从服务器,并在查询时通过 db() 或模型方法触发对应连接 —— 没有中间件或全局路由层帮你判断“这个 SQL 是读还是写”,一切由你控制连接来源。
如何配置主从数据库连接
ThinkPHP 6.x 使用 database.php 中的 connections 配置多组连接,读写分离本质是定义一个主库(write)和至少一个从库(read),再让框架按策略选择:
-
write连接必须存在,且只能有一个;用于insert、update、delete等写操作 -
read可以是数组,支持多个从库,框架默认轮询(round-robin)选取 - 必须设置
'deploy' => 1启用部署模式,否则read/write配置被忽略 -
'master_balance' => ['weight' => [1, 1]]可选,用于加权轮询,但注意权重数组长度需与read数组一致
示例片段(config/database.php):
'mysql' => [
'deploy' => 1,
'rw_separate' => true,
'master_num' => 1,
'slave_no' => '',
'hostname' => '192.168.1.10',
'database' => 'test',
'username' => 'root',
'password' => '123456',
'hostport' => '3306',
'charset' => 'utf8mb4',
'prefix' => '',
'write' => [
'hostname' => '192.168.1.10',
],
'read' => [
['hostname' => '192.168.1.11'],
['hostname' => '192.168.1.12'],
],
],
什么时候走从库?哪些操作会被强制发往主库?
ThinkPHP 不解析 SQL 语义,而是靠方法名做简单分类:调用 save()、insert()、update()、delete()、execute() 等明确写意图的方法时,强制使用 write 连接;其余如 select()、find()、count()、value() 默认走 read 连接(前提是启用了 rw_separate)。
立即学习“PHP免费学习笔记(深入)”;
-
Db::table('user')->where('id', 1)->find()→ 从read中选一台执行 -
UserModel::create(['name' => 'a'])→ 必走write -
Db::query('SELECT NOW()')→ 走read(query()默认读) -
Db::execute('UPDATE user SET status=1')→ 强制走write - 事务内所有操作(包括
select)全部走主库,避免主从延迟导致数据不一致
常见踩坑点:延迟、事务、手动指定连接
主从延迟是最容易被忽视的实际问题。ThinkPHP 不做延迟检测,也不提供“强一致性读”开关,一旦业务要求“刚写完立刻能读到”,就必须绕过读写分离:
- 用
Db::connect('mysql.write')显式指定主库连接,而不是依赖自动路由 - 在事务中不要指望从库返回最新数据,
Db::transaction()内所有查询都命中主库 -
rw_separate和deploy必须同时为true,漏掉任一都会退化为单库连接 - 从库连接失败时,默认会抛出异常并中断请求,不会自动 fallback 到主库 —— 想实现容错得自己封装重试逻辑
- 使用
Db::raw()或原生 SQL 时,务必确认方法调用链:用query()还是execute(),否则可能误走从库执行写语句
真正麻烦的从来不是配通读写分离,而是厘清哪些读可以容忍延迟、哪些必须读主库、以及事务边界是否覆盖了关键一致性检查 —— 这些没法靠配置解决,得一行行看业务逻辑。



















