咱們公司使用的php框架是Yii2,最近由於線上出現了數據庫鏈接被打滿,致使線上業務出現異常的狀況,就專門研究了下Yii的數據庫連接問題。php
咱們項目中使用的是主從分離的mysql數據庫。使用的是配置中的components
的方式,其中每一個數據庫的配置都是一個component
。這樣就將數據庫的配置註冊到Yii::$app中,能夠在model中直接在getDb
中獲取數據庫的配置信息,以下mysql
$config = [ "components" => [ "exam" => [ "class" => "yii\db\Connection", //使用的數據庫鏈接類 "charset" => "utf8", //使用的鏈接編碼方式 "enableSchemaCache" => true, //啓用數據庫schema的緩存 "schemaCacheDuration" => 3600, //緩存時間1小時 "username" => "", //用戶名 "dsn" => "", "masterConfig" => [...], //主庫配置 "masters" => [], //主庫dsn列表 "slaveConfig" => [...], //從庫配置, "slaves" => [], //從庫dsn配置 ] ] ];
這樣咱們就配置了一個exam
的數據庫,而在實際的model中時,能夠在這個數據庫的基類中主從使用的數據庫配置:nginx
class Exam extends \yii\db\ActiveRecord { public static function getDb() { return \Yii::$app->exam; } }
數據庫中每一個表都有一個本身的model類,繼承這個基類,這樣在數據庫表中執行的數據庫操做就會自動的使用這個數據庫的配置。
同時,在進行數據庫操做時,框架會自動的進行數據庫的讀寫分離,插入、更新和刪除的操做會使用主庫,查詢操做會自動使用從庫。當須要在主庫查詢時,也能夠使用getDb()->useMaster()
來從主庫進行查詢。
在獲取從庫鏈接的時候,若是獲取從庫鏈接失敗,而且容許獲取主庫(默認是容許自動調整的),就會自動獲取主庫的鏈接。sql
一、在請求到來以後,Yii2進行初始化的時候創建鏈接。
這個是不可能的,咱們的項目配置了幾十個數據庫,而每次請求來了的時候不可能爲每一個數據庫創建一個鏈接,這是一種浪費:一方面一個請求不可能用到全部的數據庫;另外一方面創建了數據庫鏈接又不使用,對數據庫的資源也是一種浪費。
二、在執行數據庫model方法的時候
這樣只有在使用到相應數據庫查詢的時候,纔會創建該數據庫的鏈接,這樣能夠儘量的優化數據庫資源的使用。
同時在一個請求中,若是已經創建了一個數據庫的鏈接,接下來執行相似操做時就會複用這個數據庫的鏈接,減小數據庫連接的創建。數據庫
在創建數據庫鏈接的時候,最底層調用的方法時Yii2\db\Conection
類中的openFromPool
方法,從方法名看,是從池子中打開一個鏈接,其實這裏是從以前配置的數據庫列表中隨機獲取一個鏈接:緩存
protected function openFromPool(array $pool, array $sharedConfig) { if (empty($pool)) { return null; } if (!isset($sharedConfig['class'])) { $sharedConfig['class'] = get_class($this); } $cache = is_string($this->serverStatusCache) ? Yii::$app->get($this->serverStatusCache, false) : $this->serverStatusCache; //這裏會先將從庫的地址列表打亂,而後循環這個配置列表,逐一創建鏈接,直到創建鏈接成功。若是都創建失敗會返回null shuffle($pool); foreach ($pool as $config) { ... try { //這裏就是實際的創建鏈接的方法 $db->open(); return $db; } } return null;
這裏有一個要注意的點,在調用openFromPool
創建鏈接的時候,底層使用的實際方法是open
。這裏要注意,在獲取從庫時,是使用生成的$db
對象來創建的,而獲取主庫時直接使用的$this
,因此在open方法中會判斷是否有$this->masters
,只有在主庫時纔會有值,纔會創建主庫的鏈接。
這裏最開始覺得時每次都創建主庫鏈接,直到後來看到$db
才發現不是最開始的Connection對象了。php框架
上面說了,咱們是在執行數據庫表對應的model文件的方法時,纔會創建數據庫鏈接,而這個數據庫的鏈接會被放到db對象中,其中從庫會放到_slave
裏面,主庫的鏈接放到pdo
裏面。這樣在獲取的時候,若是有值,就會直接使用。
具體的銷燬是在php請求完畢以後,當php把返回值發送給nginx以後,php釋放相關的對象值時。
最後,使用Yii2時仍是推薦使用主從數據庫配置的,這樣能夠減小主服務器的壓力,避免形成主服務器的問題,同時,若是查詢壓力太大的時候,能夠很方便的擴展從庫,增長系統的抗壓能力。服務器