Laravel 8 运行时全局切换数据库连接,GET请求走只读库方案不生效
Laravel 8 读写连接自动切换中间件解决方案
问题原因
你之前尝试的方案不生效,核心是Laravel会将首次解析的数据库连接实例缓存在容器中,仅修改默认连接配置不会清除已实例化的连接,因此还是会复用之前的主库连接。
前置配置检查
首先确认config/database.php中已经正确配置两个数据库连接:
'connections' => [ // 主库连接(写) 'mysql' => [ 'driver' => 'mysql', 'host' => env('DB_HOST', '127.0.0.1'), 'port' => env('DB_PORT', '3306'), 'database' => env('DB_DATABASE', 'forge'), 'username' => env('DB_USERNAME', 'forge'), 'password' => env('DB_PASSWORD', ''), 'unix_socket' => env('DB_SOCKET', ''), 'charset' => 'utf8mb4', 'collation' => 'utf8mb4_unicode_ci', 'prefix' => '', 'strict' => true, 'engine' => null, ], // 只读从库连接(读) 'mysql_readonly' => [ 'driver' => 'mysql', 'host' => env('DB_READ_HOST', '127.0.0.1'), 'port' => env('DB_PORT', '3306'), 'database' => env('DB_DATABASE', 'forge'), 'username' => env('DB_READ_USERNAME', 'forge'), 'password' => env('DB_READ_PASSWORD', ''), 'unix_socket' => env('DB_SOCKET', ''), 'charset' => 'utf8mb4', 'collation' => 'utf8mb4_unicode_ci', 'prefix' => '', 'strict' => true, 'engine' => null, ], ],
实现步骤
1. 创建中间件
执行命令生成中间件文件:php artisan make:middleware DbConnectionSwitch
2. 编写中间件逻辑
编辑app/Http/Middleware/DbConnectionSwitch.php文件,内容如下:
<?php namespace App\Http\Middleware; use Closure; use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\DB; class DbConnectionSwitch { public function handle($request, Closure $next) { $currentDefaultConn = Config::get('database.default'); // GET请求走只读库,其余请求走主库 $targetConn = $request->isMethod('GET') ? 'mysql_readonly' : 'mysql'; if ($currentDefaultConn !== $targetConn) { // 清除原有连接缓存,强制使用新配置创建连接 DB::purge($currentDefaultConn); Config::set('database.default', $targetConn); } // 如需排除部分GET接口走主库,可在此处添加白名单逻辑 // if (in_array($request->path(), ['api/write-log', 'admin/*'])) { // DB::purge($targetConn); // Config::set('database.default', 'mysql'); // } return $next($request); } }
3. 注册全局中间件
打开app/Http/Kernel.php,将中间件添加到全局中间件数组的最顶部,确保在所有数据库操作执行前生效:
protected $middleware = [ \App\Http\Middleware\DbConnectionSwitch::class, // 原有其他全局中间件保持不变 // \App\Http\Middleware\TrustProxies::class, // \Fruitcake\Cors\HandleCors::class, // ... ];
注意事项
- 只读库账号仅授予SELECT权限,避免误写操作。
- 不要在GET请求中执行事务操作,事务依赖写权限,会直接报错。
- 如果Eloquent模型中单独指定了
$connection属性,需要自行调整模型的连接逻辑,或者移除$connection属性走默认连接。
内容的提问来源于stack exchange,提问作者Sunny Kumar
相关产品推荐
相关产品推荐

