Laravel 5 配置数据库主从读写分离和源码分析

9 篇文章 1 订阅

原文地址:Laravel5配置读写分离和源码分析

 

 

 

一,配置过程:

Laravel5读写分离配置比较简单,只需修改config/database.php,下面以MySQL数据库为例 内容如下 'mysql' => [

 
  1. 'read' => [

  2. 'host' => '192.168.1.1'

  3. ],

  4. 'write' => [

  5. 'host' => '196.168.1.2'

  6. ],

  7. 'driver' => 'mysql',

  8. 'database' => 'database',

  9. 'username' => 'root',

  10. 'password' => '',

  11. 'charset' => 'utf8',

  12. 'collation' => 'utf8_unicode_ci',

  13. '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是否配置了读写分离数据库

 
  1. /**

  2. * Establish a PDO connection based on the configuration.

  3. *

  4. * @param array $config

  5. * @param string $name

  6. * @return \Illuminate\Database\Connection

  7. */

  8. public function make(array $config, $name = null)

  9. {

  10. $config = $this->parseConfig($config, $name);

  11. // 如果配置了读写分离,则同时创建读库和写库的链接

  12. if (isset($config['read'])) {

  13. return $this->createReadWriteConnection($config);

  14. }

  15. // 如果没有配置,默认创建单个数据库链接

  16. return $this->createSingleConnection($config);

  17. }

2,看看如何创建读库和写库的链接

 
  1. /**

  2. * Create a single database connection instance.

  3. *

  4. * @param array $config

  5. * @return \Illuminate\Database\Connection

  6. */

  7. protected function createReadWriteConnection(array $config)

  8. {

  9. // 获取写库的配置信息,并创建链接

  10. $connection = $this->createSingleConnection($this->getWriteConfig($config));

  11. // 创建读库的链接

  12. return $connection->setReadPdo($this->createReadPdo($config));

  13. }

3,多个读库会选择哪个呢

 
  1. /**

  2. * Get the read configuration for a read / write connection.

  3. *

  4. * @param array $config

  5. * @return array

  6. */

  7. protected function getReadConfig(array $config)

  8. {

  9. $readConfig = $this->getReadWriteConfig($config, 'read');

  10.  
  11. // 如果数组即多个读库,那么通过随机函数array_rand()挑一个,默认取第一个

  12. if (isset($readConfig['host']) && is_array($readConfig['host'])) {

  13. $readConfig['host'] = count($readConfig['host']) > 1

  14. ? $readConfig['host'][array_rand($readConfig['host'])]

  15. : $readConfig['host'][0];

  16. }

  17. return $this->mergeReadWriteConfig($config, $readConfig);

  18. }

4,写库也是随机选择的

 
  1. /**

  2. * Get a read / write level configuration.

  3. *

  4. * @param array $config

  5. * @param string $type

  6. * @return array

  7. */

  8. protected function getReadWriteConfig(array $config, $type)

  9. {

  10.  
  11. // 如果多个,那么通过随机函数array_rand()挑一个

  12. if (isset($config[$type][0])) {

  13. return $config[$type][array_rand($config[$type])];

  14. }

  15. return $config[$type];

  16. }

总结:

如图1所示,可以设置多个读库和多个写库,或者不同组合,比如一个写库两个读库

2,每次只创建一个读库链接和一个写库链接,从多个库中随机选择一个;

四,源码分析:调用选择时先判断使用读库还是写库,而插入/更新/删除统一使用写库

主要文件:Illuminate / Database / Connection.php看看几个重要的函数

1,选择函数根据第三个输入参数判断使用读库还是写库

 
  1. /**

  2. * Run a select statement against the database.

  3. *

  4. * @param string $query

  5. * @param array $bindings

  6. * @param bool $useReadPdo

  7. * @return array

  8. */

  9. public function select($query, $bindings = [], $useReadPdo = true)

  10. {

  11. return $this->run($query, $bindings, function ($me, $query, $bindings) use ($useReadPdo) {

  12. if ($me->pretending()) {

  13. return [];

  14. }

  15.  
  16. // For select statements, we'll simply execute the query and return an array

  17. // of the database result set. Each element in the array will be a single

  18. // row from the database table, and will either be an array or objects.

  19. // 根据$useReadPdo参数,判断使用读库还是写库;

  20. // true使用读库,false使用写库;默认使用读库

  21. $statement = $this->getPdoForSelect($useReadPdo)->prepare($query);

  22.  
  23. $statement->execute($me->prepareBindings($bindings));

  24.  
  25. $fetchArgument = $me->getFetchArgument();

  26.  
  27. return isset($fetchArgument) ?

  28. $statement->fetchAll($me->getFetchMode(), $fetchArgument, $me->getFetchConstructorArgument()) :

  29. $statement->fetchAll($me->getFetchMode());

  30. });

  31. }

  32.  
  33. /**

  34. * Get the PDO connection to use for a select query.

  35. *

  36. * @param bool $useReadPdo

  37. * @return \PDO

  38. */

  39. protected function getPdoForSelect($useReadPdo = true)

  40. {

  41. // 根据$useReadPdo参数,选择PDO即判断使用读库还是写库;

  42. // true使用读库getReadPdo,false使用写库getPdo;

  43. return $useReadPdo ? $this->getReadPdo() : $this->getPdo();

  44. }

2,插入/更新/删除统一使用写库

 
  1. /**

  2. * Run an insert statement against the database.

  3. *

  4. * @param string $query

  5. * @param array $bindings

  6. * @return bool

  7. */

  8. public function insert($query, $bindings = [])

  9. {

  10. return $this->statement($query, $bindings);

  11. }

  12.  
  13. /**

  14. * Run an update statement against the database.

  15. *

  16. * @param string $query

  17. * @param array $bindings

  18. * @return int

  19. */

  20. public function update($query, $bindings = [])

  21. {

  22. return $this->affectingStatement($query, $bindings);

  23. }

  24.  
  25. /**

  26. * Run a delete statement against the database.

  27. *

  28. * @param string $query

  29. * @param array $bindings

  30. * @return int

  31. */

  32. public function delete($query, $bindings = [])

  33. {

  34. return $this->affectingStatement($query, $bindings);

  35. }

  36.  
  37. /**

  38. * Execute an SQL statement and return the boolean result.

  39. *

  40. * @param string $query

  41. * @param array $bindings

  42. * @return bool

  43. */

  44. public function statement($query, $bindings = [])

  45. {

  46. return $this->run($query, $bindings, function ($me, $query, $bindings) {

  47. if ($me->pretending()) {

  48. return true;

  49. }

  50.  
  51. $bindings = $me->prepareBindings($bindings);

  52. // 直接调用写库

  53. return $me->getPdo()->prepare($query)->execute($bindings);

  54. });

  55. }

  56.  
  57. /**

  58. * Run an SQL statement and get the number of rows affected.

  59. *

  60. * @param string $query

  61. * @param array $bindings

  62. * @return int

  63. */

  64. public function affectingStatement($query, $bindings = [])

  65. {

  66. return $this->run($query, $bindings, function ($me, $query, $bindings) {

  67. if ($me->pretending()) {

  68. return 0;

  69. }

  70.  
  71. // For update or delete statements, we want to get the number of rows affected

  72. // by the statement and return that back to the developer. We'll first need

  73. // to execute the statement and then we'll use PDO to fetch the affected.

  74. // 直接调用写库

  75. $statement = $me->getPdo()->prepare($query);

  76.  
  77. $statement->execute($me->prepareBindings($bindings));

  78.  
  79. return $statement->rowCount();

  80. });

  81. }

总结:

1,getReadPdo()获得读库链接,getPdo()获得写库链接;

2,选择()函数根据第三个参数判断使用读库还是写库;

五,强制使用写库

有时候,我们需要读写实时一致,写完数据库后,想马上读出来,那么读写都指定一个数据库即可。虽然Laravel5配置了读写分离,但也提供了另外的方法强制读写使用同一个数据库。

实现原理:上面$这个 - >选择()时指定使用写库的链接,即第三个参数useReadPdo设置为假即可

有几个方法可实现1,调用方法DB :: table('posts') - > selectFromWriteConnection('*') - > where('id',$ id);

源码解释:通过selectFromWriteConnection()函数主要文件:Illuminate / Database / Connection.php

 
  1. /**

  2. * Run a select statement against the database.

  3. *

  4. * @param string $query

  5. * @param array $bindings

  6. * @return array

  7. */

  8. public function selectFromWriteConnection($query, $bindings = [])

  9. {

  10. // 上面有解释$this->select()函数的第三个参数useReadPdod的意义

  11. // 第三个参数是 false,所以 select 时会使用写库,而不是读库

  12. return $this->select($query, $bindings, false);

  13. }

2,调用方法

用户:: onWriteConnection() - >查找($ ID);

源码解释:通过onWriteConnection()函数主要文件:Illuminate / Database / Eloquent / Model

 
  1. /**

  2. * Begin querying the model on the write connection.

  3. *

  4. * @return \Illuminate\Database\Query\Builder

  5. */

  6. public static function onWriteConnection()

  7. {

  8. $instance = new static;

  9. // query builder 指定使用写库

  10. return $instance->newQuery()->useWritePdo();

  11. }

看看查询构建器如何指定使用写库主要文件:Illuminate / Database / Query / Builder

 
  1. /**

  2. * Use the write pdo for query.

  3. *

  4. * @return $this

  5. */

  6. public function useWritePdo()

  7. {

  8. // 指定使用写库,useWritePdo 为true

  9. $this->useWritePdo = true;

  10.  
  11. return $this;

  12. }

  13.  
  14. /**

  15. * Run the query as a "select" statement against the connection.

  16. *

  17. * @return array

  18. */

  19. protected function runSelect()

  20. {

  21. // 执行select时,useWritePdo原值为true,这里取反,被改成false;

  22. // 即$this->select()函数第三个参数为false,所以使用写库;

  23. return $this->connection->select($this->toSql(), $this->getBindings(), ! $this->useWritePdo);

  24. }

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值