深度解析mysql登錄原理
使用mysql數據庫的第一步必然是建立連接登錄,然後在上面執行SQL命令。無論是通過mysql的客戶端,還是通過C-API,JDBC標准接口連接數據庫,這個過程一定少不了。今天我們聊一聊mysql登陸具體過程,裡面會涉及client與server的交互,並通過tcpdump抓包給大家展現這一過程。
TCP握手協議
遠程連接數據庫,mysql采用TCP協議通信,第一步是建立連接,即TCP的3次握手。mysql server端有一個監聽線程等待client請求,client發起請求後,首先發一個sync包到服務端,服務端發一個ack包作為對客戶端sync包的響應,同時發一個sync包到客戶端,最後客戶端再發一個ack包作為對服務端的響應。通過3次握手,TCP連接才算真正建立起來,這個時候mysql服務端會分配一個連接供客戶端使用。記住,TCP的3次握手都是由TCP的協議棧完成,應用程序並無感知。但是,到目前為止,整個mysql連接過程還只完成了第一步--建立TCP連接。
下面附上TCP建立連接和斷開連接的原理圖。
MYSQL握手協議
TCP連接建立成功後,mysql客戶端與mysql服務端開始進行通訊,進行mysql認證過程。(1)服務端首先會發一個握手包到客戶端,(2)然後客戶端向服務端發送認證信息(用戶名,密碼等),(3)服務端收到認證包後,會檢查用戶名與密碼是否合法,並發送包告知客戶端認證信息。如果合法,則登陸成功,否則,登陸失敗。連接報錯。有時候,我們通過show processlist看到User處於unauthenticated user ,這說明此時連接過程正處於第1步和第2步之間,服務端等待客戶端發認證信息的過程中。
15922528 unauthenticated user 10.xx.2.74:53216 NULL Connect NULL Reading from net NULL
15923418 unauthenticated user connecting host NULL Connect NULL login NULL
tcpdump抓包驗證
下面我們通過tcpdump抓網絡包來驗證我們的原理。由於測試在生產環境中進行,為了避免生產網段的IP洩露,對IP作了替換,但不影響分析過程。具體而言,10.aa.zz.142.10556代表客戶端,10.bb.yy.104.3306代表服務器端,3306是服務器的監聽端口號。
(1).在客戶端上打開tcpdump命令,監聽與10.bb.yy.104.3306的通信網絡包,命令如下:
tcpdump -S -nn -tttt -i eth0 host 10.bb.yy.104 and port 3306 and tcp -c 100
-S 將tcp的序列號以絕對值形式輸出,而不是相對值。
-nn 不進行端口名稱的轉換。
-tttt 在每一行中輸出由date處理的默認格式的時間戳。
-i eth0 指定監聽的網絡接口
host 10.bb.yy.104 and port 3306 設置監聽10.bb.yy.104:3306的網絡包
-c 100 表示監聽100包就結束。
(2).在客戶端上,利用mysql命令遠程連接服務端10.bb.yy.104,
mysql –h10.bb.yy.104 –P3306 –uxxx –pxx
登陸成功後,然後直接執行exit,退出
(3)分析tcpdump抓取的網絡包,重點分析建立TCP連接,MYSQL認證和TCP斷開連接的過程。如下圖,圖中第1部分是TCP連接建立的過程,第2部分是MYSQL認證的過程,第3部分是登陸成功後,發送基本元數據信息的過程,第4部分是斷開連接的過程。通過圖中的標示,我們可以清晰的看到TCP建立連接的3次握手,MYSQL認證以及TCP斷開連接的4次揮手過程。
建立連接
這個過程主要體現在第一部分,客戶端10.aa.zz.142.10556,首先發一個編號為1491894492的SYN包,服務端收到後,發送了1491894492+1的ACK包,並發送了一個2727774925的SYN包,最後客戶端再發送一個2727774925+1的包進行應答。
MYSQL認證
這個過程主要體現在第二部分,服務端10.bb.yy.104:3306首先發一個認證包給客戶端,然後客戶端再發送包含用戶密碼的認證包給服務器,驗證成功後,服務端最後給客戶端一個應答,那麼整個認證過程就結束了,至於第3部分是服務端與客戶端相互發送的一些元數據信息,比如版本信息之類的。
斷開連接
這個過程主要體現在第四部分,客戶端發起exit命令時,開始觸發這個動作。客戶端首先發一個編號為1491894724的FIN包,然後服務器發送一個1491894724+1的ACK包作為應答,並發送一個編號為2727775120的FIN包,最後客戶端發送2727775120+1作為應答,整個過程結束。
數據包標記解析
S=SYN 發起連接標志,一般用於建立TCP連接
P=PUSH 傳送數據標志,一般用於傳輸數據
F=FIN 關閉連接標志,一般用於關閉TCP連接
ack 表示應答包
RST= RESET 異常關閉連接
.表示沒有任何標志
源碼實現
用於MYSQL認證代碼主要集中在函數native_password_authenticate中,具體調用層次為:login_connection->check_connection->acl_authenticate->do_auth_once->native_password_authenticate,函數邏輯很簡單,就是調用write_packet往客戶端發一個認證包,然後調用read_packet等待客戶端返回包含用戶名、密碼等信息的包,最後解析包中的信息進行密碼驗證,成功後,會在調用Protocol::send_ok發一個認證成功網絡包,這個過程可以在圖中的第二步全部體現。底層socket通信代碼主要集中在sql/net_serv.cc中,具體而言讀采用接口my_net_read,寫采用接口my_net_write。
問題
(1).Unix socket方式登陸與TCP方式登陸有什麼區別和聯系?
Unix socket是實現進程間通信的一種方式,mysql支持利用Unix socket來實現客戶端-服務端的通信,但要求客戶端和服務端在同一台機器上。對於unix socket而言,同樣也是一種套接字,監聽線程會同時監聽TCP socket和Unix socket,接受到請求然後處理,後續的處理邏輯都是一致的,只不過底層通信方式不一樣罷了。
mysql -h127.0.0.1 –P3306 –uxxx –pxxx [TCP通訊方式]
mysql -uxxx –pxxx –S/usr/mysql/mysql.sock [unix socket通信方式]
(2).監聽socket是否與通信socket公用一個端口?
我們知道,服務端一直有一個監聽socket在3306端口監聽,等待新進來的客戶請求,一旦一個請求過來,服務端會重新創建一個新的通信socket,這個新的socket專門用於與這個客戶通信,而監聽socket則繼續監聽。雖然是2個套接字,但監聽socket和通信socket都是同一個端口,通過netstat可以確認這個問題。
(3).連接超時參數connect_timeout在何時作用?
這個參數實質就是在MYSQL認證過程起作用,如果在這個過程中,客戶端超過connect_timeout時間仍然沒有發送密碼認證包過來,則會主動斷開連接。