一,配置过程:
Laravel5读写分离配置比较简单,只需修改config/database.php,下面以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' => '',
]
设置完毕之后,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 来看看几个重要的函数:
1,判断database.php是否配置了读写分离数据库
-
/**
-
* 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,看看如何创建读库和写库的链接
-
/**
-
* 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,每次只创建一个读库链接和一个写库链接,从多个库中随机选择一个;
四,源码分析:调用选择时先判断使用读库还是写库,而插入/更新/删除统一使用写库
主要文件:Illuminate / Database / Connection.php看看几个重要的函数
1,选择函数根据第三个输入参数判断使用读库还是写库
-
/**
-
* 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,插入/更新/删除统一使用写库
-
/**
-
* 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,选择()函数根据第三个参数判断使用读库还是写库;
五,强制使用写库
有时候,我们需要读写实时一致,写完数据库后,想马上读出来,那么读写都指定一个数据库即可。虽然Laravel5配置了读写分离,但也提供了另外的方法强制读写使用同一个数据库。
实现原理:上面$这个 - >选择()时指定使用写库的链接,即第三个参数useReadPdo设置为假即可
有几个方法可实现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,调用方法
用户:: onWriteConnection() - >查找($ 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();
-
}
看看查询构建器如何指定使用写库主要文件: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);
-
}