聊聊數據庫(MySql)鏈接吧,你真的清楚嗎?

前言

說到數據庫鏈接,這個你們都很熟悉了。可是熟悉通常來自於下面三種狀況
* 剛開始學編程的時候,老師就說用完的數據庫鏈接必定要關閉,否則會有嚴重的後果。
* 編程一段時間後,你們都說要用鏈接池來優化數據庫鏈接。
* 編程幾年後,老大們說要考慮一臺服務器mysql的併發鏈接數與負載等。
因此不停留在據說的層面,深刻去學習與研究下mysql的鏈接機制與.net mysql驅動對鏈接的管理也挺有必要的。mysql

本文會用到哪些工具

  • vs創建一個web項目,測試鏈接的創建與回收
  • navicate for mysql(能夠看到mysql的鏈接數的實時監控)
  • nuget安裝mysql.data.dll,.net mysql的驅動
  • **.net reflector* 反編譯查看mysql驅動的源碼
  • fiddler友情客串,測測併發

打開navicate for mysql->選擇「工具」->服務器監控->勾選要監控的服務器
就能夠看到這個服務器的實時鏈接狀況,每隔5秒刷新一次。
每打開一個鏈接多一個,打開數據庫的時候對一個,打開查詢的時候也會多一個

如圖的:1463 1465 1466就是剛纔打開的。web


一.先說說mysql的最大鏈接數

在navicate中查詢:show variables like '%max_connections%';
能夠看到默認的數值爲:max_connections:151spring

爲了便於測試咱們修改my.ini文件中的max_connections參數,修改成10。必定要重啓mysql服務才能生效sql


而後咱們運行web項目,當打開頁面的時候執行。注意這裏註釋了關閉鏈接語句:

這時咱們在chrome中訪問頁面,當打開第8個頁面時出現:Too many connections的異常。chrome

爲何時第8個呢:由於以前說了打開數據庫以及查詢佔了3個,因此8+3>10。天然就報mysql異常了。
咱們看看服務器的監控:

都鏈接了2000多秒,還在一直連着,致使其餘請求沒法創建鏈接。這就是不釋放鏈接的最直接的後果。數據庫

下面咱們來手動結束那些沒有被關閉的鏈接(右擊鏈接->結束進程或者中止vs運行)。
接下來測試正常狀況,取消鏈接關閉的註釋編程

二.你關閉鏈接的時候,鏈接真的關閉了嗎?

咱們發現訪問網頁後,服務器中多了一個鏈接,進程id爲1479。可是明明執行了connection.Close(),但結果好像不是咱們預料的那樣。
等待幾十秒後發現隨着訪問的結束,這個鏈接的閒置時間爲109秒沒有被關閉掉。
瀏覽器

但當咱們從新打開一個網頁後,發現

這個鏈接的閒置時間被重置了爲0了。
這種現象跟mysql並無太多緣由,這是由mysql 的.net驅動控制的。當咱們調用connection.Close()時,與mysql創建的socket鏈接被沒有真正的關閉掉,而是被保留並置爲空閒狀態。
當新的請求鏈接過來後,會直接使用這個鏈接而不會創建新的socket鏈接。這是mysql驅動自帶的鏈接池功能,來優化性能與資源。
咱們來翻下mysql.data的源代碼:

大部分狀況下咱們調用connection.Close()只是調用:this.SetState(ConnectionState.Close,true),改變一下鏈接的狀態而已。並不是真的關閉
真正的關閉時調用CloseFully()

mysql驅動申請的鏈接真正關閉是經過超時實現的。通過測試若是閒置300秒左右,這個鏈接就會被完全關閉了服務器

因此交互流程圖是這樣的:
網絡

三.斷網了怎麼辦?

正常狀況下天然是好,可是若是:

這時候出現的情況就比未手動關閉數據庫鏈接還要嚴重,程序已經失去控制權了。會出現一批處於空閒中的數據庫鏈接沒法被正常釋放掉。若是網絡時好時壞,就有多是一批接一批的。
因此mysql服務內部也有一個超時機制,當一個socket鏈接長期空閒的時候mysql服務端會強制關閉這個鏈接。若是等mysql的超時機制來處理的問題,
超時時間經過:show global variables like '%timeout%';

如上圖所示的wait_timeout就是控制等待空閒超時的參數,默認爲28800秒也就是8小時

四.鏈接符中配置的Min Pool Size=?;Max Pool Size=?;驅動是如何管理的?

一般在配置鏈接字符串的使用鏈接池,如咱們配置
var connection = new MySqlConnection(@"Data Source=*;port=3306; User ID=*;Password=*; database=spring;Persist Security Info=false; Connect Timeout=30;Min Pool Size=2;Max Pool Size=5;");

首先Min Pool Size=2;

運行項目後,會一次多出兩個鏈接,以下圖:

並且這兩個鏈接只要iis沒有回收,不會被銷燬

任何請求過來後。只要這兩個鏈接有空閒,就不會建立新的鏈接

接着Max Pool Size=5;

若是Min Pool Size設置的數量不夠用,也就是併發量大與2的時候,那麼mysql驅動就會申請更多的鏈接。
咱們來實際測試一下,使用fiddler咱們來模擬下「瞬間的大量請求」:

打開fiddler->瀏覽器中請求網頁->在filddler找到那個請求->按住shift->點擊菜單中的Replay->輸入50->點擊ok
這樣就會異步同時發出50個請求。


咱們看下服務器的監控信息:

這時就會同時建立5個鏈接,由於2個不夠 5個最大,因此只能建立5各鏈接供使用,後面的請求只能等其餘請求釋放鏈接之後才能進入處理。
若是你在鏈接關閉前設置:Thread.Sleep(5000),讓鏈接延遲5秒再關閉。就能明顯的感受到大量數據庫請求阻塞,由於沒有額外的鏈接來處理請求了。
那是否是實際場景中,以前講的max_connectionsMax Pool Size都設置最大好了?
固然不是,服務器的性能有限啊。你設置最大,高併發的時候鏈接限制卻是沒有,但整個服務器估計就掛了。
繼續剛纔的Max Pool Size,申請的五個鏈接,若是閒置300秒左右。有3個會被回收掉,只會保留Min Pool Size生成的2個。請看截圖

也就是鏈接符中設置的Min Pool Size會永久的生成兩個鏈接在那裏,不會被關閉。Max Pool Size只在Min Pool Size不夠的時候生成,用完(閒置超時後)會被關閉掉
程序中不管多少併發請求,mysql驅動提供給當前應用的鏈接數都不會超過Max Pool Size。這樣看起來Max Pool Size也有保護服務器不被拖垮的做用

未完待續……

相關文章
相關標籤/搜索