數據庫主從和讀寫分離的配置和使用方法
數據庫主從和讀寫分離的配置和使用方法
TP:
ThinkPHP是一個開源的PHP框架,是為了簡化企業級應用開發和敏捷WEB應用開發而誕生的。ThinkPHP可以支持windows/Unix/Liunx等服務器環境,正式版需要PHP5.0以上版本支持,支持MySql、PgSQL、Sqlite以及PDO等多種數據庫
ThinkPHP內置了分布式數據庫的支持,包括主從式數據庫的讀寫分離,但是分布式數據庫必須是相同的數據庫類型。
配置DB_DEPLOY_TYPE
為1 可以采用分布式數據庫支持。如果采用分布式數據庫,定義數據庫配置信息的方式如下:
在讀寫分離的情況下,默認第一個數據庫配置是主服務器的配置信息,負責寫入數據,如果設置了DB_MASTER_NUM
參數,則可以支持多個主服務器寫入。其它的都是從數據庫的配置信息,負責讀取數據,數量不限制。每次連接從服務器並且進行讀取操作的時候,系統會隨機進行在從服務器中選擇。
還可以設置DB_SLAVE_NO
指定某個服務器進行讀操作。
小結:
TP框架內置的分布式數據庫處理,在默認的情況下,如果沒有設置DB_MASTER_NUM
參數,就會是默認第一個數據庫作為寫操作,其他是讀數據庫;如果設置了DB_MASTER_NUM
參數,例如上面的數據庫配置信息里面把DB_MASTER_NUM
參數設置為2,在沒有並發的情況下,寫操作一直會是192.168.33.111這個數據庫
YII2
主從和讀寫分離
許多數據庫支持數據庫復制來獲得更好的數據庫可用性,以及更快的服務器響應時間。 通過數據庫復制功能, 數據從所謂的主服務器被復制到從服務器。 所有的寫和更新必須發生在主服務器上, 而讀可以發生在從服務器上。
為了利用數據庫復制並且完成讀寫分離,你可以按照下面的方法來配置 yii\db\Connection 組件:
上述的配置指定了一主多從的設置。這些從庫其中之一將被建立起連接並執行讀操作, 而主庫將被用來執行寫操作。 這樣的讀寫分離將通過上述配置自動地完成。 比如,
Info: 通過調用 yii\db\Command::execute() 來執行的語句都被視為寫操作, 而其他所有通過調用 yii\db\Command 中任一 "query" 方法來執行的語句都被視為讀操作。你可以通過 Yii::$app->db->slave
來獲取當前有效的從庫連接
Connection
組件支持從庫間的負載均衡和失效備援, 當第一次執行讀操作時, Connection
組件將隨機地挑選出一個從庫並嘗試與之建立連接, 如果這個從庫被發現為”掛掉的“, 將嘗試連接另一個從庫。如果沒有一個從庫是連接得上的, 那么將試着連接到主庫上。 通過配置 server statuscache,一個“掛掉的”服務器將會被記住, 因此,在一個 yii\db\Connection::serverRetryInterval內將不再試着連接該服務器。
Info: 在上面的配置中, 每個從庫都共同地指定了 10 秒的連接超時時間, 這意味着,如果一個從庫在 10 秒內不能被連接上, 它將被視為“掛掉的”。 你可以根據你的實際環境來調整該參數。
你也可以配置多主多從。例如,
上述配置指定了兩個主庫和兩個從庫。 Connection
組件在主庫之間,也支持如從庫間般的負載均衡和失效備援。 唯一的差別是, 如果沒有主庫可用,將拋出一個異常
注意: 當你使用 masters 屬性來配置一個或多個主庫時, 所有其他指定數據庫連接的屬性 (例如 dsn
, username
, password
) 與Connection
對象本身將被忽略。
默認情況下, 事務使用主庫連接,一個事務內, 所有的數據庫操作都將使用主庫連接, 例如,
如果你想在從庫上開啟事務,你應該明確地像下面這樣做:
有時,你或許想要強制使用主庫來執行讀查詢。 這可以通過 useMaster()
方法來完成:
Laravel5
Laravel5讀寫分離配置比較簡單,只需修改config/database.PHP,下面以MySQL數據庫為例 內容如下 'mysql' => [
]
設置完畢之后,Laravel5默認將select的語句讓read指定的數據庫執行,insert/update/delete則交給write指定的數據庫,達到讀寫分離的作用。 這些設置對原始查詢raw queries,查詢生成器query builder,以及對象映射 Eloquent 都生效。 官網解釋如下:
Sometimes you maywish to use one database connection for SELECT statements, and another forINSERT, UPDATE, and DELETE statements. Laravel makes this a breeze, and theproper connections will always be used whether you are using raw queries, thequery builder, or the Eloquent ORM
二,實現原理
Laravel5讀寫分離主要有兩個過程: 第一步,根據database.php配置,創建寫庫和讀庫的鏈接connection 第二步,調用select時先判斷使用讀庫還是寫庫,而insert/update/delete統一使用寫庫
三,源碼分析:根據database.php配置,創建寫庫和讀庫的鏈接connection
主要文件:Illuminate/Database/Connectors/ConnectionFactory.php來看看幾個重要的函數:
1,判斷database.php是否配置了讀寫分離數據庫
2,看看如何創建讀庫和寫庫的鏈接
3,多個讀庫會選擇哪個呢
4,寫庫也是隨機選擇的
總結:
1,可以設置多個讀庫和多個寫庫,或者不同組合,比如一個寫庫兩個讀庫
2,每次只創建一個讀庫鏈接和一個寫庫鏈接,從多個庫中隨機選擇一個;
四,源碼分析:調用select時先判斷使用讀庫還是寫庫,而insert/update/delete統一使用寫庫
主要文件:Illuminate/Database/Connection.php看看幾個重要的函數
1,select函數根據第三個輸入參數判斷使用讀庫還是寫庫
2, insert/update/delete統一使用寫庫
總結:
1,getReadPdo()獲得讀庫鏈接,getPdo()獲得寫庫鏈接;
2,select()函數根據第三個參數判斷使用讀庫還是寫庫;
五,強制使用寫庫
有時候,我們需要讀寫實時一致,寫完數據庫后,想馬上讀出來,那么讀寫都指定一個數據庫即可。雖然Laravel5配置了讀寫分離,但也提供了另外的方法強制讀寫使用同一個數據庫。
實現原理:上面$this->select()時指定使用寫庫的鏈接,即第三個參數useReadPdo設置為false即可
有幾個方法可實現 1,調用方法 DB::table('posts')->selectFromWriteConnection('*')->where('id',$id);
源碼解釋:通過selectFromWriteConnection()函數主要文件:Illuminate/Database/Connection.php
2,調用方法
User::onWriteConnection()->find($id);
源碼解釋:通過onWriteConnection()函數主要文件:Illuminate/Database/Eloquent/Model
看看querybuilder如何指定使用寫庫 主要文件:Illuminate/Database/Query/Builder
CI
Codeigniter怎么實現讀寫分離,並且需要滿足以下兩點:
1、讀寫分離對開發應該透明。
網上有方案通過手動load多個DB來實現讀寫分離,這樣的分離跟業務關聯太緊,增加了開發難度也不利於維護,我們要做的是默認讀重庫,寫則寫主庫,讀寫分離對開發者透明
2、配置簡單。
保留現有的配置方式,通過增加一個數組來配置讀寫分離,不影響原有使用方式。
思路
1、要實現讀寫分離最簡單的思路就是在最終執行查詢的地方根據查詢語句判斷是插入主庫還是讀取從庫,所以需要找到該函數。
2、應該只連接一次數據庫,下次操作該鏈接應當可復用。也就是連一次重庫后所有的讀操作都可用,不需再次連接,主庫同理。所以我們可以將鏈接放在CI超級對象中。
3、主從的判斷是根據最終執行的SQL語句來判斷的,所以數據庫配置中的自動鏈接autoinit參數就不用設置為true了,如果默認連接了而又不需要操作該庫就浪費資源了。
4、模型中可以使用$this->db來直接操作查詢,不需要其他調整。
5、不直接修改system下的文件
實現讀寫分離
CI的DB類固定為讀取system下的文件,我們可以通過適當的重寫來實現。首先是Loader.php,其中的database方法用來加載數據庫對象,固定引用了system/database/DB.php文件,我們判斷下是否存在自定義DB.php文件,存在則引入。
重寫Loader.php
接着我們在application/core下創建database/DB.php,該文件只有一個DB方法,用來讀取配置文件並進行初始化工作。同樣有兩處地方需要重寫下:
整個DB.php調整的也基本上是文件的引入,group name的引入是為了方便后面的判斷,不引入則可以通過主機、數據庫名稱這些來配置。如果想強制關閉autoint,可以在DB.php中刪掉下面這段:
接下來就是最核心的地方。根據查詢語句實現讀寫分離。
DB_driver.php中的simple_query方法可以理解為最后執行SQL語句的方法,我們可以在這里進行數據庫鏈接的判斷。
重寫DB_driver.php
到這里讀寫分離即基本實現了,但做事情得善始善終,鏈接的數據庫對象需要關閉,可以在公用控制器中執行完畢后關掉連接。
DB_driver.php中也有close方法,可以考慮下是否可以在該方法中關閉?這里認為是不行的。
關閉數據庫鏈接
模型中的使用,為了使每個model中都可使用$this->db,以及不多次連接數據庫,這里也是將鏈接放在CI超級對象中。這里就算不讀寫分離也可以這么處理,可以很方便的連接多個DB,具體的model要使用其他庫只需要在構造函數中傳入group name即可。
模型調整
最后的數據庫配置方式,只需要在原有的基礎上配置一個數組即可。是使用雙主還是一主多從就看這里的配置方式。最開始想到直接在原配置上加鍵名來處理,但主與從的對應關系還是沒有這樣子明了,這里的定義方式決定了load_db_proxy_setting的實現方式。
database.php配置
最開始的數據庫鏈接並未放到CI超級對象中,發現load多個模型時每次都會打開鏈接,所以完成讀寫分離之后一定要測試,可以在數據庫鏈接打開和關閉的地方查看是否按預期執行(方法對應application/core/database/drivers/mysql/mysql_driver.php中的db_connect和_close)。整個調整過程最重要的兩點就是simple_query方法以及構造函數中關閉數據庫鏈接。模型中的調整是為了更方便的鏈接多個庫,未實現讀寫分離時也是這么調整的,常用的方法獨立成一個文件,MY_Model去繼承。