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