如何配置RedBean PHP实现MySQL主从数据库自动读写分离?
RedBean ORM 实现MySQL主从自动读写分离
RedBean本身没有内置的读写分离机制,但可以通过扩展其核心组件实现自动切换主从库的需求——写入操作走主库,读取操作随机/轮询走从库,无需在业务代码里手动切换。
实现步骤
初始化多数据库连接
先把主库和所有从库的连接注册到RedBean中,给每个连接分配唯一标识:// 注册主库 R::addDatabase('master', 'mysql:host=主库地址;dbname=你的数据库名', '用户名', '密码'); // 注册从库(可扩展多个) R::addDatabase('slave1', 'mysql:host=从库1地址;dbname=你的数据库名', '用户名', '密码'); R::addDatabase('slave2', 'mysql:host=从库2地址;dbname=你的数据库名', '用户名', '密码'); // 默认选中一个从库作为初始连接 R::selectDatabase('slave1');自定义QueryWriter拦截查询操作
重写RedBean的QueryWriter类,在执行SQL前判断操作类型,自动切换到对应库:class RWQueryWriter extends RedBeanPHP\QueryWriter\MySQL { // 标记是否处于事务中 private static $inTransaction = false; // 事务操作强制走主库 public function beginTransaction() { self::$inTransaction = true; R::selectDatabase('master'); return parent::beginTransaction(); } public function commit() { self::$inTransaction = false; return parent::commit(); } public function rollback() { self::$inTransaction = false; return parent::rollback(); } // 拦截所有SQL执行,判断操作类型切换库 public function exec($sql, $bindings = []) { // 事务中强制使用主库 if (self::$inTransaction) { R::selectDatabase('master'); return parent::exec($sql, $bindings); } // 判断是否为写入类操作 $isWrite = preg_match('/^(INSERT|UPDATE|DELETE|REPLACE|ALTER|DROP)/i', trim($sql)); if ($isWrite) { // 写入操作切主库 R::selectDatabase('master'); } else { // 读取操作随机选一个从库(可替换为轮询算法) $slaves = ['slave1', 'slave2']; $selectedSlave = $slaves[array_rand($slaves)]; R::selectDatabase($selectedSlave); } return parent::exec($sql, $bindings); } } // 替换RedBean默认的QueryWriter $toolbox = R::getToolbox(); $toolbox->setWriter(new RWQueryWriter($toolbox->getDatabaseAdapter())); R::configureFacadeWithToolbox($toolbox);
关键注意事项
- 事务一致性:所有事务必须在主库执行,上面的代码已经处理了事务状态下的强制主库切换,避免事务跨库导致的问题。
- 从库负载均衡:示例中用
array_rand()随机选从库,你也可以改成轮询算法(比如维护一个静态索引,每次递增取模),优化从库的负载分配。 - 强一致性读取:如果写入后需要立刻读取最新数据(避免从库同步延迟),可以封装一个手动切换主库的方法,比如:
function R_masterSelect($sql, $bindings = []) { $currentDb = R::getDatabase(); R::selectDatabase('master'); $result = R::getAll($sql, $bindings); R::selectDatabase($currentDb); return $result; } - 从库扩展:新增从库时,只需要在初始化连接的地方添加
R::addDatabase(),并更新$slaves数组即可,无需修改业务代码。
内容的提问来源于stack exchange,提问作者Francesco
相关产品推荐
相关产品推荐

