Phalcon框架和Yaf相似,是一款用C实现的拓展级别的框架,不过其功能实现更加丰富,设计思路基于依赖注入、容器等方式,更符合现代框架思想。本文主要针对Phalcon框架数据库层的读写分离进行说明,权当记录。php
既然须要主从分离,那么数据库链接至少得有两个,即将主库和从库分别做为服务注册到di容器,以下html
// app/config/services.php use Phalcon\Db\Adapter\Pdo\Mysql; // 设置主库 $di->setShared('dbMaster', function () { $params = [ 'host' => '127.0.0.1', 'username' => 'root', 'password' => 'root', 'dbname' => 'phalcon_test', 'charset' => 'utf8', 'port' => 3306, ]; return new Mysql($params); });
// 设置从库 本地测试 和主库用一个mysql实例便可 $di->setShared('dbSlave', function () { $params = [ 'host' => '127.0.0.1', 'username' => 'root', 'password' => 'root', 'dbname' => 'phalcon_test', 'charset' => 'utf8', 'port' => 3306, ]; return new Mysql($params); });
基于Model初始化时的读写分离设置mysql
Model配置以下,例子是写在每一个Model的initialize
方法中,固然你也能够统一写到BaseModel中,而后每一个Model继承便可。git
// app/models/Users.php use Phalcon\Mvc\Model; class Users extends Model { /** * 设置Model主从切换 */ public function initialize() { // 写操做 使用dbMaster链接 $this->setWriteConnectionService('dbMaster'); // 读操做 使用dbSlave链接 $this->setReadConnectionService('dbSlave'); } /** * 表名 */ public function getSource() { return 'table_xxx'; } }
基于modelsManager实现的读写分离github
一、首先须要自定义modelsManager实现,用于替换框架自带的Managersql
// app/library/ModelsManager.php use Phalcon\Mvc\Model\Manager; class ModelsManager extends Manager { /** * 读操做 返回dbSlave * * @param \Phalcon\Mvc\ModelInterface $model * * @return \Phalcon\Db\Adapter\Pdo\Mysql */ public function getReadConnection(\Phalcon\Mvc\ModelInterface $model) { return $this->getDI()->get('dbSlave'); } /** * 写操做 返回dbMaster * * @param \Phalcon\Mvc\ModelInterface $model * * @return \Phalcon\Db\Adapter\Pdo\Mysql */ public function getWriteConnection(\Phalcon\Mvc\ModelInterface $model) { return $this->getDI()->get('dbMaster'); } }
二、将自定义modelsManage注册到di容器数据库
// app/config/services.php use app/library/ModelsManager; /** * 设置自定义modelsManager */ $di->setShared( 'modelsManager', new ModelsManager() );
按照上述方法实现后,你所用到的Phalcon实现的SQL查询都可自动实现主从切换,包括
Query
、QueryBuilder
、AR
、PHQL
等。mvc
固然,若是你某些情境下须要强制指定主库或者从库操做怎么办,以下便可:app
// di容器 $di = \Phalcon\Di\FactoryDefault::getDefault(); // 获取主库连接直接操做 $di->get('dbMaster')->execute('update table_xxx set age = 100 where id = 1'); // 获取从库连接直接操做 $di->get('dbSlave')->query('select * from table_xxx');
空口无凭,我怎么知道全部的读操做都发生在了从库上,全部的写操做都发生在了主库上;别担忧,每次查询的时候写个日志记录一下不就完了。框架
须要更改最上面注册dbMaster
和dbSlave
时的逻辑,以下:
// app/config/services.php use Phalcon\Db\Adapter\Pdo\Mysql; // 设置主库 $di->setShared('dbMaster', function () { $params = [ 'host' => '127.0.0.1', 'username' => 'root', 'password' => 'root', 'dbname' => 'phalcon_test', 'charset' => 'utf8', 'port' => 3306, ]; $connection = new Mysql($params); // 记录每次sql查询日志 $logger = new \Phalcon\Logger\Adapter\File('/tmp/phalcon_sqls.log'); $eventsManager = new \Phalcon\Events\Manager(); // 注册记录sql的event $eventsManager->attach('db', function ($event, $connection) use ($logger) { ($event->getType() == 'beforeQuery') && $logger->log('Master: ' . $connection->getRealSQLStatement()); }); // 设置事件管理器 $connection->setEventsManager($eventsManager); return $connection; }); // 设置从库 本地测试 和主库用一个mysql实例便可 $di->setShared('dbSlave', function () { $params = [ 'host' => '127.0.0.1', 'username' => 'root', 'password' => 'root', 'dbname' => 'phalcon_test', 'charset' => 'utf8', 'port' => 3306, ]; $connection = new Mysql($params); // 记录每次sql查询日志 $logger = new \Phalcon\Logger\Adapter\File('/tmp/phalcon_sqls.log'); $eventsManager = new \Phalcon\Events\Manager(); // 注册记录sql的event $eventsManager->attach('db', function ($event, $connection) use ($logger) { ($event->getType() == 'beforeQuery') && $logger->log('Slave: ' . $connection->getRealSQLStatement()); }); // 设置事件管理器 $connection->setEventsManager($eventsManager); return $connection; });
完成以后查看/tmp/phalcon_sqls.log
日志,以下,即表明成功进行了分离
[Wed, 16 May 18 11:18:43 +0800][DEBUG] Slave: SELECT `table_xxx`.`id` FROM `table_xxx` [Wed, 16 May 18 11:18:43 +0800][DEBUG] Master: UPDATE `table_xxx` SET `name` = 'xxx'
我的以为两种方法实现各有优劣,须要结合本身的业务进行选取.
建议若是只是为了使用主从切换功能的话,方法二会更适合,性能会更高,毕竟实际业务可能没有那么多切换库的逻辑。
上述两种方法能够同时存在,但优先级是方法二 > 方法一,即modelsManager
的优先级高于Model
中定义。本质缘由是由于Model中执行SQL查询时,也会向modelsManager索取db实例,框架源代码文件以下
// phalcon/mvc/model.zep /** * Gets the connection used to read data for the model */ public function getReadConnection() -> <AdapterInterface> { var transaction; let transaction = <TransactionInterface> this->_transaction; if typeof transaction == "object" { return transaction->getConnection(); } return (<ManagerInterface> this->_modelsManager)->getReadConnection(this); } /** * Gets the connection used to write data to the model */ public function getWriteConnection() -> <AdapterInterface> { var transaction; let transaction = <TransactionInterface> this->_transaction; if typeof transaction == "object" { return transaction->getConnection(); } return (<ManagerInterface> this->_modelsManager)->getWriteConnection(this); }
能够看出读写Model进行查询时db实例的获取会经过modelsManager的getReadConnection
和getWriteConnection
方法实现,默认状况下Manager会返回Model注册时的实例,即方法一的实现,但若是同时使用了方法二,会直接覆盖本来Manager的实现,不会再使用Model中注册的逻辑。
来自:http://www.cnblogs.com/cheng6018/p/9341823.html