當前位置: 妍妍網 > 碼農

騰訊二面:為什麽資料庫連線很消耗資源?我竟然答不上來。。一下懵了!

2024-05-21碼農

來源:blog.csdn.net/lmy86263/article/details/76165714

👉 歡迎 ,你將獲得: 專屬的計畫實戰 / Java 學習路線 / 一對一提問 / 學習打卡 / 每月贈書

新計畫: 仿小紅書 (微服務架構)正在更新中... , 全棧前後端分離部落格計畫 2.0 版本完結啦, 演示連結 http://116.62.199.48/ 。全程手摸手,後端 + 前端全棧開發,從 0 到 1 講解每個功能點開發步驟,1v1 答疑,直到計畫上線。 目前已更新了261小節,累計41w+字,講解圖:1806張,還在持續爆肝中.. 後續還會上新更多計畫,目標是將Java領域典型的計畫都整一波,如秒殺系統, 線上商城, IM即時通訊,Spring Cloud Alibaba 等等,

開發應用程式久了,總想刨根問底,尤其對一些有公共答案的問題。大家都能解釋,但是追根究底,都解釋不清。凡是都有為什麽,而且用數位說明問題是最直觀的。

本文主要想探究一下連線資料庫的細節,尤其是在 Web 套用中要使用資料庫來連線池,以免每次發送一次請求就重新建立一次連線。對於這個問題,答案都是一致的,建立資料庫連線很耗時,但是這個耗時是都多少呢,又是分別在哪些方面產生的耗時呢?

本文以連線 MySQL 資料庫為例,因為 MySQL 資料庫是開源的,其通訊協定是公開的,所以我們能夠詳細分析建立連線的整個過程。

在本文中,消耗資源的分析主要集中在網路上,當然,資源也包括記憶體、CPU等計算資源,使用的程式語言是 Java ,但是不排除程式語言也會有一定的影響。

首先先看一下連線資料庫的 Java 程式碼,如下:

class.forName("com.mysql.jdbc.Driver");
String name = "xttblog2";
String password = "123456";
String url = "jdbc:mysql://172.16.100.131:3306/xttblog2";
Connection conn = DriverManager.getConnection(url, name, password);
// 之後程式終止,連線被強制關閉

然後透過 「Wireshark」 分析整個連線的建立過程,如下:

Wireshark抓包

在上圖中顯示的連線過程中,可以看出 MySQL 的通訊協定是基於 TCP 傳輸協定的,而且該協定是二進制協定,不是類似於 HTTP 的文本協定,其中建立連線的過程具體如下:

  • 第1步:建立 TCP 連線,透過三次握手實作;

  • 第2步:伺服器發送給客戶端 「握手資訊」 ,客戶端響應該握手訊息;

  • 第3步:客戶端 「發送認證包」 ,用於使用者驗證,驗證成功後,伺服器返回OK響應,之後開始執行命令;

  • 使用者驗證成功之後,會進行一些連線變量的設定,比如字元集、是否自動送出事務等,其間會有多次數據的互動。完成了這些步驟後,才會執行真正的數據查詢和更新等操作。

    在本文的測試中,只用了5行程式碼來建立連線,但是並沒有透過該連線去執行任何操作,所以在程式執行完畢之後,連線不是透過 Connection.close() 關閉的,而是由於程式執行完畢,導致行程終止,造成與資料庫的連線異常關閉,所以最後會出現 TCP RST 報文。在這個最簡單的程式碼中,沒有設定任何額外的連線內容,所以在設定內容上占用的時間可以認為是最少的(其實,雖然我們沒有設定任何內容,但是驅動仍然設定了字元集、事務自動送出等,這取決於具體的驅動實作),所以整個連線所使用的時間可以認為是最少的。但從統計資訊中可以看出,在不包括最後 TCP RST 報文時(因為該報文不需要伺服器返回任何響應),但是其中仍需在客戶端和伺服器之間進行往返 「7」 次, 「也就是說完成一次連線,可以認為,數據在客戶端和伺服器之間需要至少往返7次」 ,從時間上來看,從開始TCP的三次握手,到最終連線強制斷開為止(不包括最後的 RST 報文),總共花費了:

    10.416042 - 10.190799 = 0.225243s = **225.243ms**!!!

    這意味著,建立一次資料庫連線需要225ms,而這還是還可以認為是最少的,當然 「花費的時間可能受到網路狀況、資料庫伺服器效能以及套用程式碼是否高效的影響」 ,但是這裏只是一個最簡單的例子,已經足夠說明問題了!

    由於上面是程式異常終止了,但是在正常的應用程式中,連線的關閉一般都是透過 Connection.close() 完成的,程式碼如下:

    class.forName("com.mysql.jdbc.Driver");
    String name = "shine_user";
    String password = "123";
    String url = "jdbc:mysql://172.16.100.131:3306/clever_mg_test";
    Connection conn = DriverManager.getConnection(url, name, password);
    conn.close();

    這樣的話,情況發生了變化,主要體現在與資料庫連線的斷開,如下圖:

    網路抓包

  • 第1步:此時處於 MySQL 通訊協定階段,客戶端發送關閉連線請求,而且不用等待伺服端的響應;

  • 第2步:TCP斷開連線,4次揮手完成連線斷開;

  • 這裏是完整地完成了從資料庫連線的建立到關閉,整個過程花費了:

    747.284311 - 747.100954 = 0.183357s = 183.357ms

    這裏可能也有網路狀況的影響,比上述的225ms少了,但是也幾乎達到了200ms的級別。

    那麽問題來了,想象一下這個場景,對於一個日活2萬的網站來說,假設每個使用者只會發送5個請求,那麽一天就是10萬個請求,對於建立資料庫連線,我們保守一點計算為150ms好了,那麽一天當中花費在建立資料庫連線的時間有(還不包括執行查詢和更新操作):

    100000 * 150ms = 15000000ms = 15000s = 250min = 4.17h

    也就說每天花費在建立資料庫連線上的時間已經達到 「4個小時」 ,所以說資料庫連線池是必須的嘛,而且當日活增延長,單單使用資料庫連線池也不能完全保證你的服務能夠正常執行,還需要考慮其他的解決方案:

  • 緩存

  • SQL 的預編譯

  • 負載均衡

  • ……

  • 當然這不是本文的主要內容, 「本文想要闡述的核心思想只有一個,資料庫連線真的很耗時,所以不要頻繁的建立連線」

    👉 歡迎 ,你將獲得: 專屬的計畫實戰 / Java 學習路線 / 一對一提問 / 學習打卡 / 每月贈書

    新計畫: 仿小紅書 (微服務架構)正在更新中... , 全棧前後端分離部落格計畫 2.0 版本完結啦, 演示連結 http://116.62.199.48/ 。全程手摸手,後端 + 前端全棧開發,從 0 到 1 講解每個功能點開發步驟,1v1 答疑,直到計畫上線。 目前已更新了261小節,累計41w+字,講解圖:1806張,還在持續爆肝中.. 後續還會上新更多計畫,目標是將Java領域典型的計畫都整一波,如秒殺系統, 線上商城, IM即時通訊,Spring Cloud Alibaba 等等,


    1. 

    2. 

    3. 

    4. 

    最近面試BAT,整理一份面試資料Java面試BATJ通關手冊,覆蓋了Java核心技術、JVM、Java並行、SSM、微服務、資料庫、數據結構等等。

    獲取方式:點「在看」,關註公眾號並回復 Java 領取,更多內容陸續奉上。

    PS:因公眾號平台更改了推播規則,如果不想錯過內容,記得讀完點一下在看,加個星標,這樣每次新文章推播才會第一時間出現在你的訂閱列表裏。

    「在看」支持小哈呀,謝謝啦