說到數據庫鏈接,這個你們都很熟悉了。可是熟悉通常來自於下面三種狀況
* 剛開始學編程的時候,老師就說用完的數據庫鏈接必定要關閉,否則會有嚴重的後果。
* 編程一段時間後,你們都說要用鏈接池來優化數據庫鏈接。
* 編程幾年後,老大們說要考慮一臺服務器mysql的併發鏈接數與負載等。
因此不停留在據說的層面,深刻去學習與研究下mysql的鏈接機制與.net mysql驅動對鏈接的管理也挺有必要的。mysql
打開navicate for mysql->選擇「工具」->服務器監控->勾選要監控的服務器
就能夠看到這個服務器的實時鏈接狀況,每隔5秒刷新一次。
每打開一個鏈接多一個,打開數據庫的時候對一個,打開查詢的時候也會多一個
如圖的:1463 1465 1466就是剛纔打開的。web
在navicate中查詢:show variables like '%max_connections%';
能夠看到默認的數值爲:max_connections:151
spring
爲了便於測試咱們修改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小時
一般在配置鏈接字符串的使用鏈接池,如咱們配置
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;");
運行項目後,會一次多出兩個鏈接,以下圖:
並且這兩個鏈接只要iis沒有回收,不會被銷燬。
任何請求過來後。只要這兩個鏈接有空閒,就不會建立新的鏈接。
若是Min Pool Size
設置的數量不夠用,也就是併發量大與2的時候,那麼mysql驅動就會申請更多的鏈接。
咱們來實際測試一下,使用fiddler咱們來模擬下「瞬間的大量請求」:
打開fiddler->瀏覽器中請求網頁->在filddler找到那個請求->按住shift->點擊菜單中的Replay->輸入50->點擊ok
這樣就會異步同時發出50個請求。
咱們看下服務器的監控信息:
這時就會同時建立5個鏈接,由於2個不夠 5個最大,因此只能建立5各鏈接供使用,後面的請求只能等其餘請求釋放鏈接之後才能進入處理。
若是你在鏈接關閉前設置:Thread.Sleep(5000)
,讓鏈接延遲5秒再關閉。就能明顯的感受到大量數據庫請求阻塞,由於沒有額外的鏈接來處理請求了。
那是否是實際場景中,以前講的max_connections
與Max 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
也有保護服務器不被拖垮的做用
未完待續……