Yii3.0 实现高可用读写分离需配置 masterConfig/slaveConfig 及 failover 策略,强制 read_only=true 并依赖框架自动路由读写请求,主从故障时可自动降级与重试。

要在 Yii3.0 中实现真正高可用的数据库读写分离,必须让框架自动识别读/写请求类型,并在主库故障时允许从库临时承接写操作、从库全部失效时主库可降级为读节点——这些能力不能靠手动切换,必须由配置驱动运行时行为。
配置主从连接池与故障转移策略
打开 config/db.php,定义一个支持读写分离的 Connection 组件:
将原 'db' 组件替换为以下结构,注意 class 必须是 【yii\db\Connection】,且 masterConfig/slaveConfig 必须显式声明,否则 Yii3 不会启用读写分离逻辑:
'db' => [
'class' => 'yii\db\Connection',
'masterConfig' => [
'username' => 'root',
'password' => 'your_master_pass',
'charset' => 'utf8mb4',
'attributes' => [PDO::ATTR_TIMEOUT => 5],
],
'masters' => [
['dsn' => 'mysql:host=172.31.80.31;dbname=app_main'],
],
'slaveConfig' => [
'username' => 'replica',
'password' => 'your_slave_pass',
'charset' => 'utf8mb4',
'attributes' => [PDO::ATTR_TIMEOUT => 3],
'read_only' => true,
],
'slaves' => [
['dsn' => 'mysql:host=172.31.80.32;dbname=app_main'],
['dsn' => 'mysql:host=172.31.80.33;dbname=app_main'],
],
'failover' => [
'masterFallbackOnWriteFailure' => true,
'slaveFallbackOnReadFailure' => true,
'retryInterval' => 30,
'maxRetryAttempts' => 2,
],
],
其中 【'read_only' => true】 是硬性要求:它强制从库连接使用只读模式,防止应用层误发写语句导致数据错乱;若从库 MySQL 实例未开启 read_only=ON,该参数无法兜底,必须同步配置 MySQL 层。
启用自动读写路由
Yii3 默认对 AR 查询(如 find()、one()、all())自动走从库,对 save()、delete()、updateAll() 等写操作自动走主库。但以下两类操作需额外处理:
① 原生 SQL 查询必须显式标注读/写意图:
读操作用 Yii::$app->db->createCommand($sql)->queryAll() → 自动路由到从库;
写操作必须用 Yii::$app->db->createCommand($sql)->execute() → 自动路由到主库;
② 跨事务强一致性查询(如“先查再更新”)需强制走主库,否则可能读到过期数据:
在查询前插入 Yii::$app->db->open(true),【true 表示强制连接主库】,后续所有命令直到事务结束都绑定该连接。
验证主从状态与切换行为
方法一:通过调试工具触发模拟故障
临时关闭从库服务器(如 systemctl stop mysql),然后执行 Article::find()->limit(10)->asArray()->all();若返回结果且无异常,说明 slaveFallbackOnReadFailure 生效,请求已自动切回主库读取;
方法二:检查当前活跃连接
在任意控制器中添加:var_dump(Yii::$app->db->getMaster()->getActiveDSN(), Yii::$app->db->getSlave()->getActiveDSN());;正常时前者显示主库 DSN,后者显示某从库 DSN;当所有从库不可达时,后者将返回主库 DSN —— 这是 failover 生效的直接证据。
方法三:查看日志确认重试行为
在 config/web.php 的 log target 中启用 db 模块日志:'targets' => [['categories' => ['yii\db\*']] ];执行一次失败的从库查询后,日志中应出现类似 Failed to connect to slave #0 (172.31.80.32), retrying with next slave... 的记录。


















