一,配置過程:php
Laravel5讀寫分離配置比較簡單,只需修改config/database.php,下面以MySQL數據庫爲例 內容以下 'mysql' => [mysql
'read' => [ 'host' => '192.168.1.1' ], 'write' => [ 'host' => '196.168.1.2' ], 'driver' => 'mysql', 'database' => 'database', 'username' => 'root', 'password' => '', 'charset' => 'utf8', 'collation' => 'utf8_unicode_ci', 'prefix' => '',
]sql
設置完畢以後,Laravel5默認將select的語句讓read指定的數據庫執行,insert/update/delete則交給write指定的數據庫,達到讀寫分離的做用。 這些設置對原始查詢raw queries,查詢生成器query builder,以及對象映射 Eloquent 都生效。 官網解釋以下: Sometimes you may wish to use one database connection for SELECT statements, and another for INSERT, UPDATE, and DELETE statements. Laravel makes this a breeze, and the proper connections will always be used whether you are using raw queries, the query builder, or the Eloquent ORM數據庫
二,實現原理數組
Laravel5讀寫分離主要有兩個過程: 第一步,根據database.php配置,建立寫庫和讀庫的連接connection 第二步,調用select時先判斷使用讀庫仍是寫庫,而insert/update/delete統一使用寫庫函數
三,源碼分析:根據database.php配置,建立寫庫和讀庫的連接connection源碼分析
主要文件:Illuminate/Database/Connectors/ConnectionFactory.php 來看看幾個重要的函數:post
1,判斷database.php是否配置了讀寫分離數據庫fetch
/** * Establish a PDO connection based on the configuration. * * @param array $config * @param string $name * @return \Illuminate\Database\Connection */ public function make(array $config, $name = null) { $config = $this->parseConfig($config, $name); // 若是配置了讀寫分離,則同時建立讀庫和寫庫的連接 if (isset($config['read'])) { return $this->createReadWriteConnection($config); } // 若是沒有配置,默認建立單個數據庫連接 return $this->createSingleConnection($config); }
2,看看如何建立讀庫和寫庫的連接ui
/** * Create a single database connection instance. * * @param array $config * @return \Illuminate\Database\Connection */ protected function createReadWriteConnection(array $config) { // 獲取寫庫的配置信息,並建立連接 $connection = $this->createSingleConnection($this->getWriteConfig($config)); // 建立讀庫的連接 return $connection->setReadPdo($this->createReadPdo($config)); }
3,多個讀庫會選擇哪一個呢
/** * Get the read configuration for a read / write connection. * * @param array $config * @return array */ protected function getReadConfig(array $config) { $readConfig = $this->getReadWriteConfig($config, 'read'); // 若是數組即多個讀庫,那麼經過隨機函數array_rand()挑一個,默認取第一個 if (isset($readConfig['host']) && is_array($readConfig['host'])) { $readConfig['host'] = count($readConfig['host']) > 1 ? $readConfig['host'][array_rand($readConfig['host'])] : $readConfig['host'][0]; } return $this->mergeReadWriteConfig($config, $readConfig); }
4,寫庫也是隨機選擇的
/** * Get a read / write level configuration. * * @param array $config * @param string $type * @return array */ protected function getReadWriteConfig(array $config, $type) { // 若是多個,那麼經過隨機函數array_rand()挑一個 if (isset($config[$type][0])) { return $config[$type][array_rand($config[$type])]; } return $config[$type]; }
總結:
1,能夠設置多個讀庫和多個寫庫,或者不一樣組合,好比一個寫庫兩個讀庫
2,每次只建立一個讀庫連接和一個寫庫連接,從多個庫中隨機選擇一個;
四,源碼分析:調用select時先判斷使用讀庫仍是寫庫,而insert/update/delete統一使用寫庫
主要文件:Illuminate/Database/Connection.php 看看幾個重要的函數
1,select函數根據第三個輸入參數判斷使用讀庫仍是寫庫
/** * Run a select statement against the database. * * @param string $query * @param array $bindings * @param bool $useReadPdo * @return array */ public function select($query, $bindings = [], $useReadPdo = true) { return $this->run($query, $bindings, function ($me, $query, $bindings) use ($useReadPdo) { if ($me->pretending()) { return []; } // For select statements, we'll simply execute the query and return an array // of the database result set. Each element in the array will be a single // row from the database table, and will either be an array or objects. // 根據$useReadPdo參數,判斷使用讀庫仍是寫庫; // true使用讀庫,false使用寫庫;默認使用讀庫 $statement = $this->getPdoForSelect($useReadPdo)->prepare($query); $statement->execute($me->prepareBindings($bindings)); $fetchArgument = $me->getFetchArgument(); return isset($fetchArgument) ? $statement->fetchAll($me->getFetchMode(), $fetchArgument, $me->getFetchConstructorArgument()) : $statement->fetchAll($me->getFetchMode()); }); } /** * Get the PDO connection to use for a select query. * * @param bool $useReadPdo * @return \PDO */ protected function getPdoForSelect($useReadPdo = true) { // 根據$useReadPdo參數,選擇PDO即判斷使用讀庫仍是寫庫; // true使用讀庫getReadPdo,false使用寫庫getPdo; return $useReadPdo ? $this->getReadPdo() : $this->getPdo(); }
2, insert/update/delete統一使用寫庫
/** * Run an insert statement against the database. * * @param string $query * @param array $bindings * @return bool */ public function insert($query, $bindings = []) { return $this->statement($query, $bindings); } /** * Run an update statement against the database. * * @param string $query * @param array $bindings * @return int */ public function update($query, $bindings = []) { return $this->affectingStatement($query, $bindings); } /** * Run a delete statement against the database. * * @param string $query * @param array $bindings * @return int */ public function delete($query, $bindings = []) { return $this->affectingStatement($query, $bindings); } /** * Execute an SQL statement and return the boolean result. * * @param string $query * @param array $bindings * @return bool */ public function statement($query, $bindings = []) { return $this->run($query, $bindings, function ($me, $query, $bindings) { if ($me->pretending()) { return true; } $bindings = $me->prepareBindings($bindings); // 直接調用寫庫 return $me->getPdo()->prepare($query)->execute($bindings); }); } /** * Run an SQL statement and get the number of rows affected. * * @param string $query * @param array $bindings * @return int */ public function affectingStatement($query, $bindings = []) { return $this->run($query, $bindings, function ($me, $query, $bindings) { if ($me->pretending()) { return 0; } // For update or delete statements, we want to get the number of rows affected // by the statement and return that back to the developer. We'll first need // to execute the statement and then we'll use PDO to fetch the affected. // 直接調用寫庫 $statement = $me->getPdo()->prepare($query); $statement->execute($me->prepareBindings($bindings)); return $statement->rowCount(); }); }
總結:
1,getReadPdo()得到讀庫連接,getPdo()得到寫庫連接;
2,select()函數根據第三個參數判斷使用讀庫仍是寫庫;
五,強制使用寫庫
有時候,咱們須要讀寫實時一致,寫完數據庫後,想立刻讀出來,那麼讀寫都指定一個數據庫便可。 雖然Laravel5配置了讀寫分離,但也提供了另外的方法強制讀寫使用同一個數據庫。
實現原理:上面$this->select()時指定使用寫庫的連接,即第三個參數useReadPdo設置爲false便可
有幾個方法可實現 1,調用方法 DB::table('posts')->selectFromWriteConnection('*')->where('id', $id);
源碼解釋:經過selectFromWriteConnection()函數 主要文件:Illuminate/Database/Connection.php
/** * Run a select statement against the database. * * @param string $query * @param array $bindings * @return array */ public function selectFromWriteConnection($query, $bindings = []) { , // 上面有解釋$this->select()函數的第三個參數useReadPdod的意義 // 第三個參數是 false,因此 select 時會使用寫庫,而不是讀庫 return $this->select($query, $bindings, false); }
2,調用方法
User::onWriteConnection()->find($id);
源碼解釋:經過onWriteConnection()函數 主要文件:Illuminate/Database/Eloquent/Model
/** * Begin querying the model on the write connection. * * @return \Illuminate\Database\Query\Builder */ public static function onWriteConnection() { $instance = new static; // query builder 指定使用寫庫 return $instance->newQuery()->useWritePdo(); }
看看query builder如何指定使用寫庫 主要文件:Illuminate/Database/Query/Builder
/** * Use the write pdo for query. * * @return $this */ public function useWritePdo() { // 指定使用寫庫,useWritePdo 爲true $this->useWritePdo = true; return $this; } /** * Run the query as a "select" statement against the connection. * * @return array */ protected function runSelect() { // 執行select時,useWritePdo原值爲true,這裏取反,被改爲false; // 即$this->select()函數第三個參數爲false,因此使用寫庫; return $this->connection->select($this->toSql(), $this->getBindings(), ! $this->useWritePdo); }