本文目录导读:

在 Laravel 中,读写分离通常不通过中间件实现,而是通过数据库配置文件或Sticky 数据库会话机制来处理,中间件并不是实现读写分离的标准或推荐方式。
具体解释如下:
为什么通常不用中间件?
- 请求粒度与 SQL 粒度不匹配:中间件作用于整个 HTTP 请求,一个请求可能包含多个数据库操作(先读后写),如果只用中间件将整个请求指向读库或写库,会导致写操作也去了只读库,或读操作去了主库,无法精细控制。
- Laravel 已内置更优雅的方案:Laravel 提供了
数据库连接池和读写分离配置,可以自动将select语句分发到从库,将insert/update/delete语句分发到主库。
Laravel 实现读写分离的标准方式
Laravel 通过 config/database.php 中的配置来实现,不需要中间件。
配置读写分离连接
在 config/database.php 的 connections 数组中,定义一个使用了读写分离的连接(mysql):
'mysql' => [
'driver' => 'mysql',
'read' => [ // 从库配置(可多个)
['host' => '192.168.1.1'],
['host' => '192.168.1.2'], // 可配置多个从库,自动轮询
],
'write' => [ // 主库配置(可多个)
['host' => '192.168.1.10'],
],
'sticky' => true, // 关键选项:粘性会话
'database' => 'your_database',
'username' => 'your_username',
'password' => 'your_password',
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
// ... 其他选项
],
工作原理
- 读操作(SELECT):Laravel 自动从
read数组中随机或轮询选择一个主机执行。 - 写操作(INSERT/UPDATE/DELETE):自动使用
write数组中的主机。 sticky选项:当设置为true时,如果当前请求中执行过写操作(写入主库),那么该请求后续的所有读操作都会强制使用主库(避免主从延迟导致刚写入的数据读不到)。
什么时候用中间件?
虽然不推荐用中间件实现读写分离本身,但以下场景可能需要中间件配合读写分离:
- 强制某个请求全部使用主库:某些需要强一致性的接口(如订单支付回调),可以在中间件中临时切换数据库连接为写库,确保读到的数据是最新的。
- 按用户或路由分发:为管理员请求提供写库,普通访客请求使用读库(但这会牺牲实时性,用户可能看到旧数据)。
示例:强制请求使用写库
// app/Http/Middleware/ForceWriteConnection.php
namespace App\Http\Middleware;
use Closure;
class ForceWriteConnection
{
public function handle($request, Closure $next)
{
// 将默认连接设为写库连接(假设写库连接名为 'mysql_write')
config(['database.default' => 'mysql_write']);
return $next($request);
}
}
然后在 app/Http/Kernel.php 中注册这个中间件。
| 需求 | 推荐方案 | 是否用中间件 |
|---|---|---|
| 标准读写分离(SELECT走从库,写走主库) | config/database.php 的 read/write 配置 |
否 |
| 防止主从延迟导致的数据不一致 | 开启 sticky => true |
否 |
| 某个请求必须读主库(强一致) | 在中间件或控制器中调用 DB::connection('mysql_write') |
可选(方便批量切换) |
| 按用户类型路由到不同数据库 | 中间件切换连接池 | 是 |
核心结论:Laravel 的读写分离功能由数据库层自动完成,不需要中间件,只有当你需要对整个请求强制切换连接时,才考虑使用中间件。