日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

yii连接mysql主从_Connection 数据库主从连接源码剖析

發布時間:2024/9/18 数据库 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 yii连接mysql主从_Connection 数据库主从连接源码剖析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

連接總體流程

隨機打亂從庫配置(master可以選擇是否打亂,slave一定會打亂;如果master沒有配置數組則直接使用$dsn和$username作為master的配置,也就是一主)

遍歷配置如果該庫的配置在serverStatusCache緩存中生效則說明過期時間內該配置不可用,直接continue

如果緩存無值則去實例化PDO(會新實例化一個類$db = Yii::createObject($config))

實例化PDO記錄info日志

記錄實例化性能分析日志(可選,根據$enableProfiling)

new PDO

設置PDO的ATTR_ERRMODE、ATTR_EMULATE_PREPARES、字符集屬性

執行afterOpen事件

實例化失敗記錄serverStatusCache緩存,標識該配置600秒(默認)內不可用

master與slave連接的差異

slave連接會先判斷是否可以使用slave從庫($this->enableSlaves)

slave如果連接不上會判斷是否進而連接master

一定會打亂slave配置數組

如果沒有master配置數組,則直接使用$this->dns和$this->root

master連接可以選擇是否打亂配置數組

涉及方法

getSlavePdo($fallbackToMaster = true),會調用getSlave(false),如果從庫連不上就去連接master(根據參數$fallbackToMaster),返回的是PDO類

getSlave($fallbackToMaster = true),會調用openFromPool,從slave配置數組中隨機連接一個slave,返回的是Connection類

getMasterPdo(),會調用open(),如果master配置數據為空則直接使用$dns進行連接,如果master配置數組不為空則遍歷連接master,返回PDO類

getMaster(),遍歷連接master,返回Connection類

openFromPool(array $pool, array $sharedConfig),隨機打亂配置信息

openFromPoolSequentially(array $pool, array $sharedConfig),不隨機打亂配置信息,遍歷配置信息,連接PDO;內部還有serverStatusCache去緩存服務器可用狀態

open(),連接master或者slave,會記錄info和Profiling日志(可選)

createPdoInstance(),實例化PDO

屬性注入

因為Connection繼承Component類,可以使用屬性注入,所以

$db->master; //和$db->getMaster();一樣

$db->slave; //和$db->getSlave();一樣

$db->masterPdo; //和$db->getMasterPdo();一樣

$db->slavePdo; //和$db->getSlavePdo();一樣

源碼細節

yii2中可以配置一主多從配置,在連接從庫方面數據庫配置如下

public function actionD(){

$db = new \yii\db\Connection([

'dsn' => 'mysql:host=192.168.124.10;dbname=test',

'username' => 'root',

'password' => '',

'charset' => 'utf8',

'enableSlaves'=>true, //可以使用從庫

'serverRetryInterval'=>600, //其中一個從庫配置不可用,將緩存不可用狀態600秒

'enableProfiling'=>true, //默認配置,將記錄連接數據庫、執行語句等的性能分析日志

'emulatePrepare'=>true, //true為開啟本地模擬prepare

'slaveConfig'=>[ //從庫slaves屬性通用配置

'username' => 'root',

'password' => '',

'attributes' => [

PDO::ATTR_TIMEOUT => 10,

],

],

'slaves'=>[ //從庫列表

["dsn"=>"mysql:host=192.168.124.11;dbname=test"],

["dsn"=>"mysql:host=192.168.124.12;dbname=test"],

[

"dsn"=>"mysql:host=192.168.124.13;dbname=test",

'username' => 'main',

'password' => '123456',

],

],

'masters'=>[ //主庫列表

["dsn"=>"mysql:host=192.168.124.11;dbname=test"],

["dsn"=>"mysql:host=192.168.124.12;dbname=test"],

[

"dsn"=>"mysql:host=192.168.124.13;dbname=test",

'username' => 'main',

'password' => '123456',

],

],

'masterConfig'=>[ //主庫master屬性通用配置

'username' => 'root',

'password' => '',

'attributes' => [

PDO::ATTR_TIMEOUT => 10,

],

],

]);

$slave = $db->getSlavePdo();

$slave = $db->getSlave();

return 123;

}

}

可以看到數據庫操作的類是\yii\db\Connection,該類繼承Component類,可見可以使用屬性注入、行為和事件

針對Connection的屬性注入,只有以下屬性是私有的,以下屬性一般不會在外部進行操作

private $_transaction;

private $_schema;

private $_driverName;

private $_master = false;

private $_slave = false;

private $_queryCacheInfo = [];

針對Connection的事件,可以注冊以下事件

const EVENT_AFTER_OPEN = 'afterOpen'; //連接數據庫后的事件

const EVENT_BEGIN_TRANSACTION = 'beginTransaction'; //開啟事務的事件

const EVENT_COMMIT_TRANSACTION = 'commitTransaction'; //提交事務的事件

const EVENT_ROLLBACK_TRANSACTION = 'rollbackTransaction'; //回滾的事件

Connection類使用的mysql操作對象是PDO,涉及方法有

public function getSlavePdo($fallbackToMaster = true)

public function getSlave($fallbackToMaster = true)

追進在getSlavePdo方法,可見當slave連接不可用時候,會默認連接主庫($fallbackToMaster=true)

public function getSlavePdo($fallbackToMaster = true){

$db = $this->getSlave(false); //進行slave連接

if ($db === null) {

return $fallbackToMaster ? $this->getMasterPdo() : null; //當slave不可用時候,是否連接主庫

}

return $db->pdo; //返回數據庫連接資源,從庫和主庫都連接不上的話會返回null

}

追進getSlave方法

public function getSlave($fallbackToMaster = true){

if (!$this->enableSlaves) {//判斷是否可以使用slave

return $fallbackToMaster ? $this : null;

}

if ($this->_slave === false) { //如果還沒有連接過slave庫,就進行連接

$this->_slave = $this->openFromPool($this->slaves, $this->slaveConfig); //將slave配置信息給openFromPool方法

}

return $this->_slave === null && $fallbackToMaster ? $this : $this->_slave;

}

追進openFromPool方法,可見該方法就是將$this->slaves從庫dsn配置打亂,讓第一次連接slave隨機化

protected function openFromPool(array $pool, array $sharedConfig){

shuffle($pool); //打亂從庫配置

return $this->openFromPoolSequentially($pool, $sharedConfig);

}

openFromPoolSequentially方法

protected function openFromPoolSequentially(array $pool, array $sharedConfig){

if (empty($pool)) { //是否有slave配置池,如果沒有的話就是最后返回給$this->_slave為null

return null;

}

if (!isset($sharedConfig['class'])) { //判斷$this->slaveConfig屬性是否有class,可以設置class將從庫的連接配置成自己重新的類

$sharedConfig['class'] = get_class($this);

}

//服務狀態緩存,使用依賴注入獲取cache緩存類

$cache = is_string($this->serverStatusCache) ? Yii::$app->get($this->serverStatusCache, false) : $this->serverStatusCache;

//遍歷slave配置池

foreach ($pool as $config) {

//合并配置

$config = array_merge($sharedConfig, $config);

if (empty($config['dsn'])) {

throw new InvalidConfigException('The "dsn" option must be specified.');

}

$key = [__METHOD__, $config['dsn']];

//這里就是判斷緩存是否有值,如果有的話說明在過期時間內該配置的slave不可用

if ($cache instanceof CacheInterface && $cache->get($key)) {

// should not try this dead server now

continue;

}

//通過依賴注入創建了一個類,該類專門是這個slave的

$db = Yii::createObject($config);

try {

$db->open();

return $db;

} catch (\Exception $e) {

//記錄日志

Yii::warning("Connection ({$config['dsn']}) failed: " . $e->getMessage(), __METHOD__);

if ($cache instanceof CacheInterface) {

//將該配置的slave服務不可用狀態存緩存,值是1,過期時間的$this->serverRetryInterval秒

$cache->set($key, 1, $this->serverRetryInterval);

}

}

}

return null;

}

在TestController控制器的配置中,可見會隨機打亂slaves屬性,如果有任何一個從庫連接上了就是直接返回,如果有連接不上的就會將不可用狀態存緩存,然后繼續循環

slaveConfig屬性是一個從庫的通用配置,會循環的去array_merge()屬性slaves

所以配置

'slaveConfig'=>[ //從庫slaves屬性通用配置

'username' => 'root',

'password' => '',

'attributes' => [

PDO::ATTR_TIMEOUT => 10,

],

],

'slaves'=>[ //從庫列表

["dsn"=>"mysql:host=192.168.124.11;dbname=test"],

["dsn"=>"mysql:host=192.168.124.12;dbname=test"],

[

"dsn"=>"mysql:host=192.168.124.13;dbname=test",

'username' => 'main',

'password' => '123456',

'class'=> yii\overload\myDB

],

]

最后生成的配置為(這個配置會被shuffle函數打亂順序)

'slaves'=>[ //從庫列表

[

"dsn"=>"mysql:host=192.168.124.11;dbname=test",

'username' => 'root',

'password' => '',

'attributes' => [

PDO::ATTR_TIMEOUT => 10,

],

'class' => 'yii\db\Connection',

],

[

"dsn"=>"mysql:host=192.168.124.12;dbname=test"

'username' => 'root',

'password' => '',

'attributes' => [

PDO::ATTR_TIMEOUT => 10,

],

'class' => 'yii\db\Connection',

],

[

"dsn"=>"mysql:host=192.168.124.13;dbname=test",

'username' => 'main',

'password' => '123456',

'class'=> yii\overload\myDB

],

]

在open方法中,因為是重新new,所以$this->pdo和$this->master都是null

public function open(){

//因為是重新new,所以$this->pdo和$this->master都是null

if ($this->pdo !== null) {

return;

}

//因為是重新new,所以$this->pdo和$this->master都是null

if (!empty($this->masters)) {

$db = $this->getMaster();

if ($db !== null) {

$this->pdo = $db->pdo;

return;

}

throw new InvalidConfigException('None of the master DB servers is available.');

}

if (empty($this->dsn)) {

throw new InvalidConfigException('Connection::dsn cannot be empty.');

}

$token = 'Opening DB connection: ' . $this->dsn;

$enableProfiling = $this->enableProfiling;

try {

//記錄日志

Yii::info($token, __METHOD__);

//如果開啟了性能分析,則記錄性能分析日志(性能分析開啟)

if ($enableProfiling) {

Yii::beginProfile($token, __METHOD__);

}

$this->pdo = $this->createPdoInstance();

$this->initConnection();

//如果開啟了性能分析,則記錄性能分析日志(性能分析關閉)

if ($enableProfiling) {

Yii::endProfile($token, __METHOD__);

}

} catch (\PDOException $e) {

if ($enableProfiling) {

Yii::endProfile($token, __METHOD__);

}

throw new Exception($e->getMessage(), $e->errorInfo, (int) $e->getCode(), $e);

}

}

在createPdoInstance方法中,這個沒什么好說的,就是執行new PDO

protected function createPdoInstance(){

$pdoClass = $this->pdoClass;

if ($pdoClass === null) {

$pdoClass = 'PDO';

if ($this->_driverName !== null) {

$driver = $this->_driverName;

} elseif (($pos = strpos($this->dsn, ':')) !== false) {

$driver = strtolower(substr($this->dsn, 0, $pos));

}

if (isset($driver)) {

if ($driver === 'mssql' || $driver === 'dblib') {

$pdoClass = 'yii\db\mssql\PDO';

} elseif ($driver === 'sqlsrv') {

$pdoClass = 'yii\db\mssql\SqlsrvPDO';

}

}

}

$dsn = $this->dsn;

if (strncmp('sqlite:@', $dsn, 8) === 0) {

$dsn = 'sqlite:' . Yii::getAlias(substr($dsn, 7));

}

return new $pdoClass($dsn, $this->username, $this->password, $this->attributes);

}

在initConnection方法中,這個也沒什么好說的,就是去設置PDO屬性和執行afterOpen事件

protected function initConnection(){

$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

if ($this->emulatePrepare !== null && constant('PDO::ATTR_EMULATE_PREPARES')) {

$this->pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, $this->emulatePrepare);

}

if ($this->charset !== null && in_array($this->getDriverName(), ['pgsql', 'mysql', 'mysqli', 'cubrid'], true)) {

$this->pdo->exec('SET NAMES ' . $this->pdo->quote($this->charset));

}

$this->trigger(self::EVENT_AFTER_OPEN);

}

主庫連接getMaterPdo()方法

public function getMasterPdo(){

$this->open();

return $this->pdo;

}

主庫連接getMaster()方法

public function getMaster(){

if ($this->_master === false) {

$this->_master = $this->shuffleMasters //是否隨機打亂master配置數組

? $this->openFromPool($this->masters, $this->masterConfig)

: $this->openFromPoolSequentially($this->masters, $this->masterConfig);

}

return $this->_master;

}

總結

以上是生活随笔為你收集整理的yii连接mysql主从_Connection 数据库主从连接源码剖析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。