日本黄色一级经典视频|伊人久久精品视频|亚洲黄色色周成人视频九九九|av免费网址黄色小短片|黄色Av无码亚洲成年人|亚洲1区2区3区无码|真人黄片免费观看|无码一级小说欧美日免费三级|日韩中文字幕91在线看|精品久久久无码中文字幕边打电话

當(dāng)前位置:首頁(yè) > > 架構(gòu)師社區(qū)
[導(dǎo)讀]最近,在開(kāi)發(fā)一個(gè)分庫(kù)分表中間件,由于功能需求,需要分析MySQL協(xié)議,發(fā)現(xiàn)網(wǎng)上對(duì)于MySQL協(xié)議分析的文章大部分都過(guò)時(shí)了,原因是分析的MySQL版本太低了。怎么辦?于是乎,我便硬著頭皮開(kāi)始啃MySQL源碼,經(jīng)過(guò)兩個(gè)多月的整理,終于總結(jié)出這篇MySQL協(xié)議!


怒肝兩個(gè)月MySQL源碼,總結(jié)出這篇2W字的MySQL協(xié)議詳解

寫(xiě)在前面

最近,在開(kāi)發(fā)一個(gè)分庫(kù)分表中間件,由于功能需求,需要分析MySQL協(xié)議,發(fā)現(xiàn)網(wǎng)上對(duì)于MySQL協(xié)議分析的文章大部分都過(guò)時(shí)了,原因是分析的MySQL版本太低了。怎么辦呢?于是乎,我便硬著頭皮開(kāi)始啃MySQL源碼,經(jīng)過(guò)兩個(gè)多月的整理,終于總結(jié)出這篇MySQL協(xié)議。

注:部分來(lái)自于互聯(lián)網(wǎng),感謝數(shù)據(jù)庫(kù)大牛前輩們的默默付出??!

交互過(guò)程

MySQL客戶(hù)端與服務(wù)器的交互主要分為兩個(gè)階段:握手認(rèn)證階段和命令執(zhí)行階段。

握手認(rèn)證階段

握手認(rèn)證階段為客戶(hù)端與服務(wù)器建立連接后進(jìn)行,交互過(guò)程如下:

  • 服務(wù)器 -> 客戶(hù)端:握手初始化消息
  • 客戶(hù)端 -> 服務(wù)器:登陸認(rèn)證消息
  • 服務(wù)器 -> 客戶(hù)端:認(rèn)證結(jié)果消息

命令執(zhí)行階段

客戶(hù)端認(rèn)證成功后,會(huì)進(jìn)入命令執(zhí)行階段,交互過(guò)程如下:

  • 客戶(hù)端 -> 服務(wù)器:執(zhí)行命令消息
  • 服務(wù)器 -> 客戶(hù)端:命令執(zhí)行結(jié)果

MySQL客戶(hù)端與服務(wù)器的完整交互過(guò)程如下


怒肝兩個(gè)月MySQL源碼,總結(jié)出這篇2W字的MySQL協(xié)議詳解

基本類(lèi)型

整型值

MySQL報(bào)文中整型值分別有1、2、3、4、8字節(jié)長(zhǎng)度,使用小字節(jié)序傳輸。

字符串(以NULL結(jié)尾)(Null-Terminated String)

字符串長(zhǎng)度不固定,當(dāng)遇到'NULL'(0x00)字符時(shí)結(jié)束。

二進(jìn)制數(shù)據(jù)(長(zhǎng)度編碼)(Length Coded Binary)

數(shù)據(jù)長(zhǎng)度不固定,長(zhǎng)度值由數(shù)據(jù)前的1-9個(gè)字節(jié)決定,其中長(zhǎng)度值所占的字節(jié)數(shù)不定,字節(jié)數(shù)由第1個(gè)字節(jié)決定,如下表:

第一個(gè)字節(jié)值 后續(xù)字節(jié)數(shù) 長(zhǎng)度值說(shuō)明
0-250 0 第一個(gè)字節(jié)值即為數(shù)據(jù)的真實(shí)長(zhǎng)度
251 0 空數(shù)據(jù),數(shù)據(jù)的真實(shí)長(zhǎng)度為零
252 2 后續(xù)額外2個(gè)字節(jié)標(biāo)識(shí)了數(shù)據(jù)的真實(shí)長(zhǎng)度
253 3 后續(xù)額外3個(gè)字節(jié)標(biāo)識(shí)了數(shù)據(jù)的真實(shí)長(zhǎng)度
254 8 后續(xù)額外8個(gè)字節(jié)標(biāo)識(shí)了數(shù)據(jù)的真實(shí)長(zhǎng)度

字符串(長(zhǎng)度編碼)(Length Coded String)

字符串長(zhǎng)度不固定,無(wú)'NULL'(0x00)結(jié)束符,編碼方式與上面的 Length Coded Binary 相同。

報(bào)文結(jié)構(gòu)

報(bào)文分為消息頭和消息體兩部分,其中消息頭占用固定的4個(gè)字節(jié),消息體長(zhǎng)度由消息頭中的長(zhǎng)度字段決定,報(bào)文結(jié)構(gòu)如下:

怒肝兩個(gè)月MySQL源碼,總結(jié)出這篇2W字的MySQL協(xié)議詳解

消息頭

報(bào)文長(zhǎng)度

用于標(biāo)記當(dāng)前請(qǐng)求消息的實(shí)際數(shù)據(jù)長(zhǎng)度值,以字節(jié)為單位,占用3個(gè)字節(jié),最大值為 0xFFFFFF,即接近 16 MB 大?。ū?6MB少1個(gè)字節(jié))。

序號(hào)

在一次完整的請(qǐng)求/響應(yīng)交互過(guò)程中,用于保證消息順序的正確,每次客戶(hù)端發(fā)起請(qǐng)求時(shí),序號(hào)值都會(huì)從0開(kāi)始計(jì)算。

消息體

消息體用于存放請(qǐng)求的內(nèi)容及響應(yīng)的數(shù)據(jù),長(zhǎng)度由消息頭中的長(zhǎng)度值決定。

報(bào)文類(lèi)型

登陸認(rèn)證交互報(bào)文

握手初始化報(bào)文(服務(wù)器 -> 客戶(hù)端)

怒肝兩個(gè)月MySQL源碼,總結(jié)出這篇2W字的MySQL協(xié)議詳解

服務(wù)協(xié)議版本號(hào):該值由 PROTOCOL_VERSION 宏定義決定(參考MySQL源代碼/include/mysql_version.h頭文件定義)

服務(wù)版本信息:該值為字符串,由 MYSQL_SERVER_VERSION 宏定義決定(參考MySQL源代碼/include/mysql_version.h頭文件定義)

服務(wù)器線程ID:服務(wù)器為當(dāng)前連接所創(chuàng)建的線程ID。

挑戰(zhàn)隨機(jī)數(shù):MySQL數(shù)據(jù)庫(kù)用戶(hù)認(rèn)證采用的是挑戰(zhàn)/應(yīng)答的方式,服務(wù)器生成該挑戰(zhàn)數(shù)并發(fā)送給客戶(hù)端,由客戶(hù)端進(jìn)行處理并返回相應(yīng)結(jié)果,然后服務(wù)器檢查是否與預(yù)期的結(jié)果相同,從而完成用戶(hù)認(rèn)證的過(guò)程。

服務(wù)器權(quán)能標(biāo)志:用于與客戶(hù)端協(xié)商通訊方式,各標(biāo)志位含義如下(參考MySQL源代碼/include/mysql_com.h中的宏定義):

標(biāo)志位名稱(chēng) 標(biāo)志位 說(shuō)明
CLIENT_LONG_PASSWORD 0x0001 new more secure passwords
CLIENT_FOUND_ROWS 0x0002 Found instead of affected rows
CLIENT_LONG_FLAG 0x0004 Get all column flags
CLIENT_CONNECT_WITH_DB 0x0008 One can specify db on connect
CLIENT_NO_SCHEMA 0x0010 Do not allow database.table.column
CLIENT_COMPRESS 0x0020 Can use compression protocol
CLIENT_ODBC 0x0040 Odbc client
CLIENT_LOCAL_FILES 0x0080 Can use LOAD DATA LOCAL
CLIENT_IGNORE_SPACE 0x0100 Ignore spaces before '('
CLIENT_PROTOCOL_41 0x0200 New 4.1 protocol
CLIENT_INTERACTIVE 0x0400 This is an interactive client
CLIENT_SSL 0x0800 Switch to SSL after handshake
CLIENT_IGNORE_SIGPIPE 0x1000 IGNORE sigpipes
CLIENT_TRANSACTIONS 0x2000 Client knows about transactions
CLIENT_RESERVED 0x4000 Old flag for 4.1 protocol
CLIENT_SECURE_CONNECTION 0x8000 New 4.1 authentication
CLIENT_MULTI_STATEMENTS 0x0001 0000 Enable/disable multi-stmt support
CLIENT_MULTI_RESULTS 0x0002 0000 Enable/disable multi-results

字符編碼:標(biāo)識(shí)服務(wù)器所使用的字符集。

服務(wù)器狀態(tài):狀態(tài)值定義如下(參考MySQL源代碼/include/mysql_com.h中的宏定義):

狀態(tài)名稱(chēng) 狀態(tài)值
SERVER_STATUS_IN_TRANS 0x0001
SERVER_STATUS_AUTOCOMMIT 0x0002
SERVER_STATUS_CURSOR_EXISTS 0x0040
SERVER_STATUS_LAST_ROW_SENT 0x0080
SERVER_STATUS_DB_DROPPED 0x0100
SERVER_STATUS_NO_BACKSLASH_ESCAPES 0x0200
SERVER_STATUS_METADATA_CHANGED 0x0400

登陸認(rèn)證報(bào)文(客戶(hù)端 -> 服務(wù)器)

MySQL 4.0 及之前的版本

怒肝兩個(gè)月MySQL源碼,總結(jié)出這篇2W字的MySQL協(xié)議詳解

MySQL 4.1 及之后的版本

怒肝兩個(gè)月MySQL源碼,總結(jié)出這篇2W字的MySQL協(xié)議詳解

客戶(hù)端權(quán)能標(biāo)志:用于與客戶(hù)端協(xié)商通訊方式,標(biāo)志位含義與握手初始化報(bào)文中的相同??蛻?hù)端收到服務(wù)器發(fā)來(lái)的初始化報(bào)文后,會(huì)對(duì)服務(wù)器發(fā)送的權(quán)能標(biāo)志進(jìn)行修改,保留自身所支持的功能,然后將權(quán)能標(biāo)返回給服務(wù)器,從而保證服務(wù)器與客戶(hù)端通訊的兼容性。

最大消息長(zhǎng)度:客戶(hù)端發(fā)送請(qǐng)求報(bào)文時(shí)所支持的最大消息長(zhǎng)度值。

字符編碼:標(biāo)識(shí)通訊過(guò)程中使用的字符編碼,與服務(wù)器在認(rèn)證初始化報(bào)文中發(fā)送的相同。

用戶(hù)名:客戶(hù)端登陸用戶(hù)的用戶(hù)名稱(chēng)。

挑戰(zhàn)認(rèn)證數(shù)據(jù):客戶(hù)端用戶(hù)密碼使用服務(wù)器發(fā)送的挑戰(zhàn)隨機(jī)數(shù)進(jìn)行加密后,生成挑戰(zhàn)認(rèn)證數(shù)據(jù),然后返回給服務(wù)器,用于對(duì)用戶(hù)身份的認(rèn)證。

數(shù)據(jù)庫(kù)名稱(chēng):當(dāng)客戶(hù)端的權(quán)能標(biāo)志位 CLIENT_CONNECT_WITH_DB 被置位時(shí),該字段必須出現(xiàn)。

客戶(hù)端命令請(qǐng)求報(bào)文(客戶(hù)端 -> 服務(wù)器)

怒肝兩個(gè)月MySQL源碼,總結(jié)出這篇2W字的MySQL協(xié)議詳解

命令:用于標(biāo)識(shí)當(dāng)前請(qǐng)求消息的類(lèi)型,例如切換數(shù)據(jù)庫(kù)(0x02)、查詢(xún)命令(0x03)等。命令值的取值范圍及說(shuō)明如下表(參考MySQL源代碼/include/mysql_com.h頭文件中的定義):

類(lèi)型值 命令 功能 關(guān)聯(lián)函數(shù)
0x00 COM_SLEEP (內(nèi)部線程狀態(tài)) (無(wú))
0x01 COM_QUIT 關(guān)閉連接 mysql_close
0x02 COM_INIT_DB 切換數(shù)據(jù)庫(kù) mysql_select_db
0x03 COM_QUERY SQL查詢(xún)請(qǐng)求 mysql_real_query
0x04 COM_FIELD_LIST 獲取數(shù)據(jù)表字段信息 mysql_list_fields
0x05 COM_CREATE_DB 創(chuàng)建數(shù)據(jù)庫(kù) mysql_create_db
0x06 COM_DROP_DB 刪除數(shù)據(jù)庫(kù) mysql_drop_db
0x07 COM_REFRESH 清除緩存 mysql_refresh
0x08 COM_SHUTDOWN 停止服務(wù)器 mysql_shutdown
0x09 COM_STATISTICS 獲取服務(wù)器統(tǒng)計(jì)信息 mysql_stat
0x0A COM_PROCESS_INFO 獲取當(dāng)前連接的列表 mysql_list_processes
0x0B COM_CONNECT (內(nèi)部線程狀態(tài)) (無(wú))
0x0C COM_PROCESS_KILL 中斷某個(gè)連接 mysql_kill
0x0D COM_DEBUG 保存服務(wù)器調(diào)試信息 mysql_dump_debug_info
0x0E COM_PING 測(cè)試連通性 mysql_ping
0x0F COM_TIME (內(nèi)部線程狀態(tài)) (無(wú))
0x10 COM_DELAYED_INSERT (內(nèi)部線程狀態(tài)) (無(wú))
0x11 COM_CHANGE_USER 重新登陸(不斷連接) mysql_change_user
0x12 COM_BINLOG_DUMP 獲取二進(jìn)制日志信息 (無(wú))
0x13 COM_TABLE_DUMP 獲取數(shù)據(jù)表結(jié)構(gòu)信息 (無(wú))
0x14 COM_CONNECT_OUT (內(nèi)部線程狀態(tài)) (無(wú))
0x15 COM_REGISTER_SLAVE 從服務(wù)器向主服務(wù)器進(jìn)行注冊(cè) (無(wú))
0x16 COM_STMT_PREPARE 預(yù)處理SQL語(yǔ)句 mysql_stmt_prepare
0x17 COM_STMT_EXECUTE 執(zhí)行預(yù)處理語(yǔ)句 mysql_stmt_execute
0x18 COM_STMT_SEND_LONG_DATA 發(fā)送BLOB類(lèi)型的數(shù)據(jù) mysql_stmt_send_long_data
0x19 COM_STMT_CLOSE 銷(xiāo)毀預(yù)處理語(yǔ)句 mysql_stmt_close
0x1A COM_STMT_RESET 清除預(yù)處理語(yǔ)句參數(shù)緩存 mysql_stmt_reset
0x1B COM_SET_OPTION 設(shè)置語(yǔ)句選項(xiàng) mysql_set_server_option
0x1C COM_STMT_FETCH 獲取預(yù)處理語(yǔ)句的執(zhí)行結(jié)果 mysql_stmt_fetch

參數(shù):內(nèi)容是用戶(hù)在MySQL客戶(hù)端輸入的命令(不包括每行命令結(jié)尾的";"分號(hào))。另外這個(gè)字段的字符串不是以NULL字符結(jié)尾,而是通過(guò)消息頭中的長(zhǎng)度值計(jì)算而來(lái)。

例如:當(dāng)我們?cè)贛ySQL客戶(hù)端中執(zhí)行use hutaow;命令時(shí)(切換到hutaow數(shù)據(jù)庫(kù)),發(fā)送的請(qǐng)求報(bào)文數(shù)據(jù)會(huì)是下面的樣子:

0x02?0x68?0x75?0x74?0x61?0x6f?0x77

其中,0x02為請(qǐng)求類(lèi)型值COM_INIT_DB,后面的0x68 0x75 0x74 0x61 0x6f 0x77為ASCII字符hutaow

COM_QUIT 消息報(bào)文

功能:關(guān)閉當(dāng)前連接(客戶(hù)端退出),無(wú)參數(shù)。

COM_INIT_DB 消息報(bào)文

功能:切換數(shù)據(jù)庫(kù),對(duì)應(yīng)的SQL語(yǔ)句為USE。

字節(jié) 說(shuō)明
n 數(shù)據(jù)庫(kù)名稱(chēng)(字符串到達(dá)消息尾部時(shí)結(jié)束,無(wú)結(jié)束符)

COM_QUERY 消息報(bào)文

功能:最常見(jiàn)的請(qǐng)求消息類(lèi)型,當(dāng)用戶(hù)執(zhí)行SQL語(yǔ)句時(shí)發(fā)送該消息。

字節(jié) 說(shuō)明
n SQL語(yǔ)句(字符串到達(dá)消息尾部時(shí)結(jié)束,無(wú)結(jié)束符)

COM_FIELD_LIST 消息報(bào)文

功能:查詢(xún)某表的字段(列)信息,等同于SQL語(yǔ)句SHOW [FULL] FIELDS FROM ...。

字節(jié) 說(shuō)明
n 表格名稱(chēng)(Null-Terminated String)
n 字段(列)名稱(chēng)或通配符(可選)

COM_CREATE_DB 消息報(bào)文

功能:創(chuàng)建數(shù)據(jù)庫(kù),該消息已過(guò)時(shí),而被SQL語(yǔ)句CREATE DATABASE代替。

字節(jié) 說(shuō)明
n 數(shù)據(jù)庫(kù)名稱(chēng)(字符串到達(dá)消息尾部時(shí)結(jié)束,無(wú)結(jié)束符)

COM_DROP_DB 消息報(bào)文

功能:刪除數(shù)據(jù)庫(kù),該消息已過(guò)時(shí),而被SQL語(yǔ)句DROP DATABASE代替。

字節(jié) 說(shuō)明
n 數(shù)據(jù)庫(kù)名稱(chēng)(字符串到達(dá)消息尾部時(shí)結(jié)束,無(wú)結(jié)束符)

COM_REFRESH 消息報(bào)文

功能:清除緩存,等同于SQL語(yǔ)句FLUSH,或是執(zhí)行mysqladmin flush-foo命令時(shí)發(fā)送該消息。

字節(jié) 說(shuō)明
1 清除緩存選項(xiàng)(位圖方式存儲(chǔ),各標(biāo)志位含義如下)

0x01: REFRESH_GRANT

0x02: REFRESH_LOG

0x04: REFRESH_TABLES

0x08: REFRESH_HOSTS

0x10: REFRESH_STATUS

0x20: REFRESH_THREADS

0x40: REFRESH_SLAVE

0x80: REFRESH_MASTER

COM_SHUTDOWN 消息報(bào)文

功能:停止MySQL服務(wù)。執(zhí)行mysqladmin shutdown命令時(shí)發(fā)送該消息。

字節(jié) 說(shuō)明
1 停止服務(wù)選項(xiàng)

0x00: SHUTDOWN_DEFAULT

0x01: SHUTDOWN_WAIT_CONNECTIONS

0x02: SHUTDOWN_WAIT_TRANSACTIONS

0x08: SHUTDOWN_WAIT_UPDATES

0x10: SHUTDOWN_WAIT_ALL_BUFFERS

0x11: SHUTDOWN_WAIT_CRITICAL_BUFFERS

0xFE: KILL_QUERY

0xFF: KILL_CONNECTION

COM_STATISTICS 消息報(bào)文

功能:查看MySQL服務(wù)的統(tǒng)計(jì)信息(例如運(yùn)行時(shí)間、每秒查詢(xún)次數(shù)等)。執(zhí)行mysqladmin status命令時(shí)發(fā)送該消息,無(wú)參數(shù)。

COM_PROCESS_INFO 消息報(bào)文

功能:獲取當(dāng)前活動(dòng)的線程(連接)列表。等同于SQL語(yǔ)句SHOW PROCESSLIST,或是執(zhí)行mysqladmin processlist命令時(shí)發(fā)送該消息,無(wú)參數(shù)。

COM_PROCESS_KILL 消息報(bào)文

功能:要求服務(wù)器中斷某個(gè)連接。等同于SQL語(yǔ)句KILL。

字節(jié) 說(shuō)明
4 連接ID號(hào)(小字節(jié)序)

COM_DEBUG 消息報(bào)文

功能:要求服務(wù)器將調(diào)試信息保存下來(lái),保存的信息多少依賴(lài)于編譯選項(xiàng)設(shè)置(debug=no|yes|full)。執(zhí)行mysqladmin debug命令時(shí)發(fā)送該消息,無(wú)參數(shù)。

COM_PING 消息報(bào)文

功能:該消息用來(lái)測(cè)試連通性,同時(shí)會(huì)將服務(wù)器的無(wú)效連接(超時(shí))計(jì)數(shù)器清零。執(zhí)行mysqladmin ping命令時(shí)發(fā)送該消息,無(wú)參數(shù)。

COM_CHANGE_USER 消息報(bào)文

功能:在不斷連接的情況下重新登陸,該操作會(huì)銷(xiāo)毀MySQL服務(wù)器端的會(huì)話(huà)上下文(包括臨時(shí)表、會(huì)話(huà)變量等)。有些連接池用這種方法實(shí)現(xiàn)清除會(huì)話(huà)上下文。

字節(jié) 說(shuō)明
n 用戶(hù)名(字符串以NULL結(jié)尾)
n 密碼(挑戰(zhàn)數(shù))

MySQL 3.23 版本:Null-Terminated String(長(zhǎng)度9字節(jié))

MySQL 4.1 版本:Length Coded String(長(zhǎng)度1+21字節(jié))
n 數(shù)據(jù)庫(kù)名稱(chēng)(Null-Terminated String)
2 字符編碼

COM_BINLOG_DUMP 消息報(bào)文

功能:該消息是備份連接時(shí)由從服務(wù)器向主服務(wù)器發(fā)送的最后一個(gè)請(qǐng)求,主服務(wù)器收到后,會(huì)響應(yīng)一系列的報(bào)文,每個(gè)報(bào)文都包含一個(gè)二進(jìn)制日志事件。如果主服務(wù)器出現(xiàn)故障時(shí),會(huì)發(fā)送一個(gè)EOF報(bào)文。

字節(jié) 說(shuō)明
4 二進(jìn)制日志數(shù)據(jù)的起始位置(小字節(jié)序)
4 二進(jìn)制日志數(shù)據(jù)標(biāo)志位(目前未使用,永遠(yuǎn)為0x00)
4 從服務(wù)器的服務(wù)器ID值(小字節(jié)序)
n 二進(jìn)制日志的文件名稱(chēng)(可選,默認(rèn)值為主服務(wù)器上第一個(gè)有效的文件名)

COM_TABLE_DUMP 消息報(bào)文

功能:將數(shù)據(jù)表從主服務(wù)器復(fù)制到從服務(wù)器中,執(zhí)行SQL語(yǔ)句LOAD TABLE ... FROM MASTER時(shí)發(fā)送該消息。目前該消息已過(guò)時(shí),不再使用。

字節(jié) 說(shuō)明
n 數(shù)據(jù)庫(kù)名稱(chēng)(Length Coded String)
n 數(shù)據(jù)表名稱(chēng)(Length Coded String)

COM_REGISTER_SLAVE 消息報(bào)文

功能:在從服務(wù)器report_host變量設(shè)置的情況下,當(dāng)備份連接時(shí)向主服務(wù)器發(fā)送的注冊(cè)消息。

字節(jié) 說(shuō)明
4 從服務(wù)器ID值(小字節(jié)序)
n 主服務(wù)器IP地址(Length Coded String)
n 主服務(wù)器用戶(hù)名(Length Coded String)
n 主服務(wù)器密碼(Length Coded String)
2 主服務(wù)器端口號(hào)
4 安全備份級(jí)別(由MySQL服務(wù)器rpl_recovery_rank變量設(shè)置,暫時(shí)未使用)
4 主服務(wù)器ID值(值恒為0x00)

COM_PREPARE 消息報(bào)文

功能:預(yù)處理SQL語(yǔ)句,使用帶有"?"占位符的SQL語(yǔ)句時(shí)發(fā)送該消息。

字節(jié) 說(shuō)明
n 帶有"?"占位符的SQL語(yǔ)句(字符串到達(dá)消息尾部時(shí)結(jié)束,無(wú)結(jié)束符)

COM_EXECUTE 消息報(bào)文

功能:執(zhí)行預(yù)處理語(yǔ)句。

字節(jié) 說(shuō)明
4 預(yù)處理語(yǔ)句的ID值
1 標(biāo)志位

0x00: CURSOR_TYPE_NO_CURSOR

0x01: CURSOR_TYPE_READ_ONLY

0x02: CURSOR_TYPE_FOR_UPDATE

0x04: CURSOR_TYPE_SCROLLABLE
4 保留(值恒為0x01)
如果參數(shù)數(shù)量大于0
n 空位圖(Null-Bitmap,長(zhǎng)度 = (參數(shù)數(shù)量 + 7) / 8 字節(jié))
1 參數(shù)分隔標(biāo)志
如果參數(shù)分隔標(biāo)志值為1
n 每個(gè)參數(shù)的類(lèi)型值(長(zhǎng)度 = 參數(shù)數(shù)量 * 2 字節(jié))
n 每個(gè)參數(shù)的值

COM_LONG_DATA 消息報(bào)文

該消息報(bào)文有兩種形式,一種用于發(fā)送二進(jìn)制數(shù)據(jù),另一種用于發(fā)送文本數(shù)據(jù)。

功能:用于發(fā)送二進(jìn)制(BLOB)類(lèi)型的數(shù)據(jù)(調(diào)用mysql_stmt_send_long_data函數(shù))。

字節(jié) 說(shuō)明
4 預(yù)處理語(yǔ)句的ID值(小字節(jié)序)
2 參數(shù)序號(hào)(小字節(jié)序)
n 數(shù)據(jù)負(fù)載(數(shù)據(jù)到達(dá)消息尾部時(shí)結(jié)束,無(wú)結(jié)束符)

功能:用于發(fā)送超長(zhǎng)字符串類(lèi)型的數(shù)據(jù)(調(diào)用mysql_send_long_data函數(shù))

字節(jié) 說(shuō)明
4 預(yù)處理語(yǔ)句的ID值(小字節(jié)序)
2 參數(shù)序號(hào)(小字節(jié)序)
2 數(shù)據(jù)類(lèi)型(未使用)
n 數(shù)據(jù)負(fù)載(數(shù)據(jù)到達(dá)消息尾部時(shí)結(jié)束,無(wú)結(jié)束符)

COM_CLOSE_STMT 消息報(bào)文

功能:銷(xiāo)毀預(yù)處理語(yǔ)句。

字節(jié) 說(shuō)明
4 預(yù)處理語(yǔ)句的ID值(小字節(jié)序)

COM_RESET_STMT 消息報(bào)文

功能:將預(yù)處理語(yǔ)句的參數(shù)緩存清空。多數(shù)情況和COM_LONG_DATA一起使用。

字節(jié) 說(shuō)明
4 預(yù)處理語(yǔ)句的ID值(小字節(jié)序)

COM_SET_OPTION 消息報(bào)文

功能:設(shè)置語(yǔ)句選項(xiàng),選項(xiàng)值為/include/mysql_com.h頭文件中定義的enum_mysql_set_option枚舉類(lèi)型:

  • MYSQL_OPTION_MULTI_STATEMENTS_ON
  • MYSQL_OPTION_MULTI_STATEMENTS_OFF
字節(jié) 說(shuō)明
2 選項(xiàng)值(小字節(jié)序)

COM_FETCH_STMT 消息報(bào)文

功能:獲取預(yù)處理語(yǔ)句的執(zhí)行結(jié)果(一次可以獲取多行數(shù)據(jù))。

字節(jié) 說(shuō)明
4 預(yù)處理語(yǔ)句的ID值(小字節(jié)序)
4 數(shù)據(jù)的行數(shù)(小字節(jié)序)

服務(wù)器響應(yīng)報(bào)文(服務(wù)器 -> 客戶(hù)端)

當(dāng)客戶(hù)端發(fā)起認(rèn)證請(qǐng)求或命令請(qǐng)求后,服務(wù)器會(huì)返回相應(yīng)的執(zhí)行結(jié)果給客戶(hù)端??蛻?hù)端在收到響應(yīng)報(bào)文后,需要首先檢查第1個(gè)字節(jié)的值,來(lái)區(qū)分響應(yīng)報(bào)文的類(lèi)型。

響應(yīng)報(bào)文類(lèi)型 第1個(gè)字節(jié)取值范圍
OK 響應(yīng)報(bào)文 0x00
Error 響應(yīng)報(bào)文 0xFF
Result Set 報(bào)文 0x01 - 0xFA
Field 報(bào)文 0x01 - 0xFA
Row Data 報(bào)文 0x01 - 0xFA
EOF 報(bào)文 0xFE

注:響應(yīng)報(bào)文的第1個(gè)字節(jié)在不同類(lèi)型中含義不同,比如在OK報(bào)文中,該字節(jié)并沒(méi)有實(shí)際意義,值恒為0x00;而在Result Set報(bào)文中,該字節(jié)又是長(zhǎng)度編碼的二進(jìn)制數(shù)據(jù)結(jié)構(gòu)(Length Coded Binary)中的第1字節(jié)。

響應(yīng)報(bào)文

客戶(hù)端的命令執(zhí)行正確時(shí),服務(wù)器會(huì)返回OK響應(yīng)報(bào)文。

MySQL 4.0 及之前的版本

字節(jié) 說(shuō)明
1 OK報(bào)文,值恒為0x00
1-9 受影響行數(shù)(Length Coded Binary)
1-9 索引ID值(Length Coded Binary)
2 服務(wù)器狀態(tài)
n 服務(wù)器消息(字符串到達(dá)消息尾部時(shí)結(jié)束,無(wú)結(jié)束符)

MySQL 4.1 及之后的版本

字節(jié) 說(shuō)明
1 OK報(bào)文,值恒為0x00
1-9 受影響行數(shù)(Length Coded Binary)
1-9 索引ID值(Length Coded Binary)
2 服務(wù)器狀態(tài)
2 告警計(jì)數(shù)
n 服務(wù)器消息(字符串到達(dá)消息尾部時(shí)結(jié)束,無(wú)結(jié)束符,可選)

受影響行數(shù):當(dāng)執(zhí)行INSERT/UPDATE/DELETE語(yǔ)句時(shí)所影響的數(shù)據(jù)行數(shù)。

索引ID值:該值為AUTO_INCREMENT索引字段生成,如果沒(méi)有索引字段,則為0x00。注意:當(dāng)INSERT插入語(yǔ)句為多行數(shù)據(jù)時(shí),該索引ID值為第一個(gè)插入的數(shù)據(jù)行索引值,而非最后一個(gè)。

服務(wù)器狀態(tài):客戶(hù)端可以通過(guò)該值檢查命令是否在事務(wù)處理中。

告警計(jì)數(shù):告警發(fā)生的次數(shù)。

服務(wù)器消息:服務(wù)器返回給客戶(hù)端的消息,一般為簡(jiǎn)單的描述性字符串,可選字段。

響應(yīng)報(bào)文

MySQL 4.0 及之前的版本

字節(jié) 說(shuō)明
1 Error報(bào)文,值恒為0xFF
2 錯(cuò)誤編號(hào)(小字節(jié)序)
n 服務(wù)器消息

MySQL 4.1 及之后的版本

字節(jié) 說(shuō)明
1 Error報(bào)文,值恒為0xFF
2 錯(cuò)誤編號(hào)(小字節(jié)序)
1 服務(wù)器狀態(tài)標(biāo)志,恒為'#'字符
5 服務(wù)器狀態(tài)(5個(gè)字符)
n 服務(wù)器消息

錯(cuò)誤編號(hào):錯(cuò)誤編號(hào)值定義在源代碼/include/mysqld_error.h頭文件中。

服務(wù)器狀態(tài):服務(wù)器將錯(cuò)誤編號(hào)通過(guò)mysql_errno_to_sqlstate函數(shù)轉(zhuǎn)換為狀態(tài)值,狀態(tài)值由5字節(jié)的ASCII字符組成,定義在源代碼/include/sql_state.h頭文件中。

服務(wù)器消息:錯(cuò)誤消息字符串到達(dá)消息尾時(shí)結(jié)束,長(zhǎng)度可以由消息頭中的長(zhǎng)度值計(jì)算得出。消息長(zhǎng)度為0-512字節(jié)。

Result Set 消息

當(dāng)客戶(hù)端發(fā)送查詢(xún)請(qǐng)求后,在沒(méi)有錯(cuò)誤的情況下,服務(wù)器會(huì)返回結(jié)果集(Result Set)給客戶(hù)端。

Result Set 消息分為五部分,結(jié)構(gòu)如下:

結(jié)構(gòu) 說(shuō)明
[Result Set Header] 列數(shù)量
[Field] 列信息(多個(gè))
[EOF] 列結(jié)束
[Row Data] 行數(shù)據(jù)(多個(gè))
[EOF] 數(shù)據(jù)結(jié)束

Result Set Header 結(jié)構(gòu)

字節(jié) 說(shuō)明
1-9 Field結(jié)構(gòu)計(jì)數(shù)(Length Coded Binary)
1-9 額外信息(Length Coded Binary)

Field結(jié)構(gòu)計(jì)數(shù):用于標(biāo)識(shí)Field結(jié)構(gòu)的數(shù)量,取值范圍0x00-0xFA。

額外信息:可選字段,一般情況下不應(yīng)該出現(xiàn)。只有像SHOW COLUMNS這種語(yǔ)句的執(zhí)行結(jié)果才會(huì)用到額外信息(標(biāo)識(shí)表格的列數(shù)量)。

Field 結(jié)構(gòu)

Field為數(shù)據(jù)表的列信息,在Result Set中,F(xiàn)ield會(huì)連續(xù)出現(xiàn)多次,次數(shù)由Result Set Header結(jié)構(gòu)中的IField結(jié)構(gòu)計(jì)數(shù)值決定。

MySQL 4.0 及之前的版本

字節(jié) 說(shuō)明
n 數(shù)據(jù)表名稱(chēng)(Length Coded String)
n 列(字段)名稱(chēng)(Length Coded String)
4 列(字段)長(zhǎng)度(Length Coded String)
2 列(字段)類(lèi)型(Length Coded String)
2 列(字段)標(biāo)志(Length Coded String)
1 整型值精度
n 默認(rèn)值(Length Coded String)

MySQL 4.1 及之后的版本

字節(jié) 說(shuō)明
n 目錄名稱(chēng)(Length Coded String)
n 數(shù)據(jù)庫(kù)名稱(chēng)(Length Coded String)
n 數(shù)據(jù)表名稱(chēng)(Length Coded String)
n 數(shù)據(jù)表原始名稱(chēng)(Length Coded String)
n 列(字段)名稱(chēng)(Length Coded String)
4 列(字段)原始名稱(chēng)(Length Coded String)
1 填充值
2 字符編碼
4 列(字段)長(zhǎng)度
1 列(字段)類(lèi)型
2 列(字段)標(biāo)志
1 整型值精度
2 填充值(0x00)
n 默認(rèn)值(Length Coded String)

目錄名稱(chēng):在4.1及之后的版本中,該字段值為"def"。

數(shù)據(jù)庫(kù)名稱(chēng):數(shù)據(jù)庫(kù)名稱(chēng)標(biāo)識(shí)。

數(shù)據(jù)表名稱(chēng):數(shù)據(jù)表的別名(AS之后的名稱(chēng))。

數(shù)據(jù)表原始名稱(chēng):數(shù)據(jù)表的原始名稱(chēng)(AS之前的名稱(chēng))。

列(字段)名稱(chēng):列(字段)的別名(AS之后的名稱(chēng))。

列(字段)原始名稱(chēng):列(字段)的原始名稱(chēng)(AS之前的名稱(chēng))。

字符編碼:列(字段)的字符編碼值。

列(字段)長(zhǎng)度:列(字段)的長(zhǎng)度值,真實(shí)長(zhǎng)度可能小于該值,例如VARCHAR(2)類(lèi)型的字段實(shí)際只能存儲(chǔ)1個(gè)字符。

列(字段)類(lèi)型:列(字段)的類(lèi)型值,取值范圍如下(參考源代碼/include/mysql_com.h頭文件中的enum_field_type枚舉類(lèi)型定義):

類(lèi)型值 名稱(chēng)
0x00 FIELD_TYPE_DECIMAL
0x01 FIELD_TYPE_TINY
0x02 FIELD_TYPE_SHORT
0x03 FIELD_TYPE_LONG
0x04 FIELD_TYPE_FLOAT
0x05 FIELD_TYPE_DOUBLE
0x06 FIELD_TYPE_NULL
0x07 FIELD_TYPE_TIMESTAMP
0x08 FIELD_TYPE_LONGLONG
0x09 FIELD_TYPE_INT24
0x0A FIELD_TYPE_DATE
0x0B FIELD_TYPE_TIME
0x0C FIELD_TYPE_DATETIME
0x0D FIELD_TYPE_YEAR
0x0E FIELD_TYPE_NEWDATE
0x0F FIELD_TYPE_VARCHAR (new in MySQL 5.0)
0x10 FIELD_TYPE_BIT (new in MySQL 5.0)
0xF6 FIELD_TYPE_NEWDECIMAL (new in MYSQL 5.0)
0xF7 FIELD_TYPE_ENUM
0xF8 FIELD_TYPE_SET
0xF9 FIELD_TYPE_TINY_BLOB
0xFA FIELD_TYPE_MEDIUM_BLOB
0xFB FIELD_TYPE_LONG_BLOB
0xFC FIELD_TYPE_BLOB
0xFD FIELD_TYPE_VAR_STRING
0xFE FIELD_TYPE_STRING
0xFF FIELD_TYPE_GEOMETRY

列(字段)標(biāo)志:各標(biāo)志位定義如下(參考源代碼/include/mysql_com.h頭文件中的宏定義):

標(biāo)志位 名稱(chēng)
0x0001 NOT_NULL_FLAG
0x0002 PRI_KEY_FLAG
0x0004 UNIQUE_KEY_FLAG
0x0008 MULTIPLE_KEY_FLAG
0x0010 BLOB_FLAG
0x0020 UNSIGNED_FLAG
0x0040 ZEROFILL_FLAG
0x0080 BINARY_FLAG
0x0100 ENUM_FLAG
0x0200 AUTO_INCREMENT_FLAG
0x0400 TIMESTAMP_FLAG
0x0800 SET_FLAG

數(shù)值精度:該字段對(duì)DECIMALNUMERIC類(lèi)型的數(shù)值字段有效,用于標(biāo)識(shí)數(shù)值的精度(小數(shù)點(diǎn)位置)。

默認(rèn)值:該字段用在數(shù)據(jù)表定義中,普通的查詢(xún)結(jié)果中不會(huì)出現(xiàn)。

:Field結(jié)構(gòu)的相關(guān)處理函數(shù):

  • 客戶(hù)端: /client/client.c源文件中的 unpack_fields函數(shù)
  • 服務(wù)器: /sql/sql_base.cc源文件中的 send_fields函數(shù)

EOF 結(jié)構(gòu)

EOF結(jié)構(gòu)用于標(biāo)識(shí)Field和Row Data的結(jié)束,在預(yù)處理語(yǔ)句中,EOF也被用來(lái)標(biāo)識(shí)參數(shù)的結(jié)束。

MySQL 4.0 及之前的版本

字節(jié) 說(shuō)明
1 EOF值(0xFE)

MySQL 4.1 及之后的版本

字節(jié) 說(shuō)明
1 EOF值(0xFE)
2 告警計(jì)數(shù)
2 狀態(tài)標(biāo)志位

告警計(jì)數(shù):服務(wù)器告警數(shù)量,在所有數(shù)據(jù)都發(fā)送給客戶(hù)端后該值才有效。

狀態(tài)標(biāo)志位:包含類(lèi)似SERVER_MORE_RESULTS_EXISTS這樣的標(biāo)志位。

:由于EOF值與其它Result Set結(jié)構(gòu)共用1字節(jié),所以在收到報(bào)文后需要對(duì)EOF包的真實(shí)性進(jìn)行校驗(yàn),校驗(yàn)條件為:

  • 第1字節(jié)值為0xFE
  • 包長(zhǎng)度小于9字節(jié)

:EOF結(jié)構(gòu)的相關(guān)處理函數(shù):

  • 服務(wù)器: protocol.cc源文件中的 send_eof函數(shù)

Row Data 結(jié)構(gòu)

在Result Set消息中,會(huì)包含多個(gè)Row Data結(jié)構(gòu),每個(gè)Row Data結(jié)構(gòu)又包含多個(gè)字段值,這些字段值組成一行數(shù)據(jù)。

字節(jié) 說(shuō)明
n 字段值(Length Coded String)
... (一行數(shù)據(jù)中包含多個(gè)字段值)

字段值:行數(shù)據(jù)中的字段值,字符串形式。

:Row Data結(jié)構(gòu)的相關(guān)處理函數(shù):

  • 客戶(hù)端: /client/client.c源文件中的 read_rows函數(shù)

Row Data 結(jié)構(gòu)(二進(jìn)制數(shù)據(jù))

該結(jié)構(gòu)用于傳輸二進(jìn)制的字段值,既可以是服務(wù)器返回的結(jié)果,也可以是由客戶(hù)端發(fā)送的(當(dāng)執(zhí)行預(yù)處理語(yǔ)句時(shí),客戶(hù)端使用Result Set消息來(lái)發(fā)送參數(shù)及數(shù)據(jù))。

字節(jié) 說(shuō)明
1 結(jié)構(gòu)頭(0x00)
(列數(shù)量 + 7 + 2) / 8 空位圖
n 字段值
... (一行數(shù)據(jù)中包含多個(gè)字段值)

空位圖:前2個(gè)比特位被保留,值分別為0和1,以保證不會(huì)和OK、Error包的首字節(jié)沖突。在MySQL 5.0及之后的版本中,這2個(gè)比特位的值都為0。

字段值:行數(shù)據(jù)中的字段值,二進(jìn)制形式。

PREPARE_OK 響應(yīng)報(bào)文(Prepared Statement)

用于響應(yīng)客戶(hù)端發(fā)起的預(yù)處理語(yǔ)句報(bào)文,組成結(jié)構(gòu)如下:

結(jié)構(gòu) 說(shuō)明
[PREPARE_OK] PREPARE_OK結(jié)構(gòu)
如果參數(shù)數(shù)量大于0
[Field] 與Result Set消息結(jié)構(gòu)相同
[EOF]
如果列數(shù)大于0
[Field] 與Result Set消息結(jié)構(gòu)相同
[EOF]

其中 PREPARD_OK 的結(jié)構(gòu)如下:

字節(jié) 說(shuō)明
1 OK報(bào)文,值為0x00
4 預(yù)處理語(yǔ)句ID值
2 列數(shù)量
2 參數(shù)數(shù)量
1 填充值(0x00)
2 告警計(jì)數(shù)

Parameter 響應(yīng)報(bào)文(Prepared Statement)

預(yù)處理語(yǔ)句的值與參數(shù)正確對(duì)應(yīng)后,服務(wù)器會(huì)返回 Parameter 報(bào)文。

字節(jié) 說(shuō)明
2 類(lèi)型
2 標(biāo)志
1 數(shù)值精度
4 字段長(zhǎng)度

類(lèi)型:與 Field 結(jié)構(gòu)中的字段類(lèi)型相同。

標(biāo)志:與 Field 結(jié)構(gòu)中的字段標(biāo)志相同。

數(shù)值精度:與 Field 結(jié)構(gòu)中的數(shù)值精度相同。

字段長(zhǎng)度:與 Field 結(jié)構(gòu)中的字段長(zhǎng)度相同。

代碼分析

議程
協(xié)議頭
協(xié)議類(lèi)型?網(wǎng)絡(luò)協(xié)議相關(guān)函數(shù)?NET緩沖?VIO緩沖?MySQL?API

協(xié)議頭

● 數(shù)據(jù)變成在網(wǎng)絡(luò)里傳輸?shù)臄?shù)據(jù),需要額外的在頭部添加4 個(gè)字節(jié)的包頭.

. packet length(3字節(jié)), 包體的長(zhǎng)度

. packet number(1字節(jié)), 從0開(kāi)始的遞增的

● sql “select 1” 的網(wǎng)絡(luò)協(xié)議是?

協(xié)議頭

● packet length三個(gè)字節(jié)意味著MySQL packet最大16M大于16M則被分包(net_write_command, my_net_write)

● packet number分包從0開(kāi)始,依次遞增.每一次執(zhí)行sql, packet_number清零(sql/net_serv.c:net_clear)

協(xié)議類(lèi)型

● handshake

● auth

● ok|error

● resultset

○ header

○ field

○ eof

○ row

● command packet

連接時(shí)的交互

協(xié)議說(shuō)明

● 協(xié)議內(nèi)字段分三種形式

○ 固定長(zhǎng)度(include/my_global.h)

■ uint*korr 解包 *

■ int*store 封包

○ length coded binary(sql-common/pack.c)

■ net_field_length 解包

■ net_store_length 封包

○ null-terminated string

● length coded binary

○ 避免binary unsafe string, 字符串的長(zhǎng)度保存在字符串的前面

■ length<251 1 byte

■ length <256^2 3 byte(第一個(gè)byte是252)

■ length<256^3 4byte(第一個(gè)byte是253)

■ else 9byte(第一個(gè)byte是254)

handshake packet

● 該協(xié)議由服務(wù)端發(fā)送客戶(hù)端

● 括號(hào)內(nèi)為字節(jié)數(shù),字節(jié)數(shù)為n為是null-terminated string;字節(jié)數(shù)為大寫(xiě)的N表示length code binary.

● salt就是scramble.分成兩個(gè)部分是為了兼容4.1版本

● sql_connect.cc:check_connection

● sql_client.c:mysql_real_connect

auth packet

● 該協(xié)議是從客戶(hù)端對(duì)密碼使用scramble加密后發(fā)送到服務(wù)端

● 其中databasename是可選的.salt就是加密后的密碼.

● sql_client.c:mysql_real_connect

● sql_connect.c:check_connection

ok packet

● ok包,命令和insert,update,delete的返回結(jié)果

● 包體首字節(jié)為0.

● insert_id, affect_rows也是一并發(fā)過(guò)來(lái).

● src/protocol.cc:net_send_ok

error packet

● 錯(cuò)誤的命令,非法的sql的返回包

● 包體首字節(jié)為255.

● error code就是CR_***,include/errmsg.h ● sqlstate marker是#

● sqlstate是錯(cuò)誤狀態(tài),include/sql_state.h

● message是錯(cuò)誤的信息

● sql/protocol.cc:net_send_error_packet

resultset packet

● 結(jié)果集的數(shù)據(jù)包,由多個(gè)packet組合而成

● 例如查詢(xún)一個(gè)結(jié)構(gòu)集,順序如下: ○ header ○ field1....fieldN ○ eof ○ row1...rowN ○ eof

● sql/client.c:cli_read_query_result

● 下面是一個(gè)sql "select * from d"查詢(xún)結(jié)果集的例子,結(jié)果 集是6行,3個(gè)字段 ○ 公式:假設(shè)結(jié)果集有N行, M個(gè)字段.則包的個(gè)數(shù)為,header(1) + field (M) + eof(1) + row(N) + eof(1) ○ 所以這個(gè)例子的MySQL packet的個(gè)數(shù)是12個(gè)

resultset packet - header

● field packet number決定了接下來(lái)的field packet的個(gè)數(shù).

● 一個(gè)返回6行記錄,3個(gè)字段的查詢(xún)語(yǔ)句

resultset packet - field

● 結(jié)果集中一個(gè)字段一個(gè)field packet.

● tables_alias是sql語(yǔ)句里表的別名,org_table才是表的真 實(shí)名字.

● sql/protocol.cc:Protocol::send_fields

● sql/client.c:cli_read_query_result

resultset packet - eof

● eof包是用于分割field packet和row packet.

● 包體首字節(jié)為254

● sql/protocol.cc:net_send_eof

resultset packet - row

● row packet里才是真正的數(shù)據(jù)包.一行數(shù)據(jù)一個(gè)packet.

● row里的每個(gè)字段都是length coded binary

● 字段的個(gè)數(shù)在header packet里

● sql/client.c:cli_read_rows

command packet

● 命令包,包括我們的sql語(yǔ)句還有一些常見(jiàn)的命令.

● 包體首字母表示命令的類(lèi)型(include/mysql_com.h),大 部分命令都是COM_QUERY.

網(wǎng)絡(luò)協(xié)議關(guān)鍵函數(shù)

● net_write_command(sql/net_serv.cc)所有的sql最終調(diào)用這個(gè)命令發(fā)送出去.

● my_net_write(sql/net_serv.cc)連接階段的socket write操作調(diào)用這個(gè)函數(shù).

● my_net_read讀取包,會(huì)判斷包大小,是否是分包

● my_real_read解析MySQL packet,第一次讀取4字節(jié),根據(jù)packet length再讀取余下來(lái)的長(zhǎng)度

● cli_safe_read客戶(hù)端解包函數(shù),包含了my_net_read

NET緩沖

● 每次socket操作都會(huì)先把數(shù)據(jù)寫(xiě),讀到net->buff,這是一 個(gè)緩沖區(qū), 減少系統(tǒng)調(diào)用調(diào)用的次數(shù).

● 當(dāng)寫(xiě)入的數(shù)據(jù)和buff內(nèi)的數(shù)據(jù)超過(guò)buff大小才會(huì)發(fā)出一次 write操作,然后再把要寫(xiě)入的buff里插入數(shù), 寫(xiě)入不會(huì) 導(dǎo)致buff區(qū)區(qū)域擴(kuò)展.(sql/net_serv.cc: net_write_buff).

● net->buff大小初始net->max_packet, 讀取會(huì)導(dǎo)致會(huì)導(dǎo)致 buff的realloc最大net->max_packet_size

● 一次sql命令的結(jié)束都會(huì)調(diào)用net_flush,把buff里的數(shù)據(jù) 都寫(xiě)到socket里.

VIO緩沖

● 從my_read_read可以看出每次packet讀取都是按需讀取, 為了減少系統(tǒng)調(diào)用,vio層面加了一個(gè)read_buffer.

● 每次讀取前先判斷vio->read_buffer所需數(shù)據(jù)的長(zhǎng)度是 否足夠.如果存在則直接copy. 如果不夠,則觸發(fā)一次 socket read 讀取2048個(gè)字(vio/viosocket.c: vio_read_buff)

MySQL API

● 數(shù)據(jù)從mysql_send_query處發(fā)送給服務(wù)端,實(shí)際調(diào)用的是 net_write_command.

● cli_read_query_result解析header packet, field packet,獲 得field_count的個(gè)數(shù)

● mysql_store_result解析了row packet,并存儲(chǔ)在result- >data里

● myql_fetch_row其實(shí)遍歷result->data

PACKET NUMBER

在做proxy的時(shí)候在這里迷糊過(guò),翻了幾遍代碼才搞明白,細(xì)節(jié)如下:??客戶(hù)端服務(wù)端的net->pkt_nr都從0開(kāi)始.接受包時(shí)比較packet number ?和net->pkt_nr是否相等,否則報(bào)packet number亂序,連接報(bào)錯(cuò);相等則pkt_nr自增.發(fā)送包時(shí)把net->pkt_nr作為packet number發(fā)送,然后對(duì)net->pkt_nr進(jìn)行自增保持和對(duì)端的同步.

接收包

sql/net_serv.c:my_real_read
?????if?(net->buff[net->where_b?+?3]?!=?(uchar)?net->pkt_nr)

發(fā)送包

sql/net_serv.c:my_net_write
???int3store(buff,len)
;
???buff[3]=?(uchar)?net->pkt_nr++;

我們來(lái)幾個(gè)具體場(chǎng)景的packet number, net->pkt_nr的變化

連接

?c?———–>?s?0??connect
?c?<—-0——s?1??handshake
?c?—–1—–>s?1??auth
?c?<—–2——s?0??ok

開(kāi)始兩方都為0,服務(wù)端發(fā)送handshake packet(pkt=0)之后自增為1,然后等待對(duì)端發(fā)送過(guò)來(lái)pkt=1的包

查詢(xún)

每次查詢(xún),服務(wù)客戶(hù)端都會(huì)對(duì)net->pkt_nr進(jìn)行清零

include/mysql_com.h
?#define?net_new_transaction(net)?((net)->pkt_nr=0)
sql/sql_parse.cc:do_command
???net_new_transaction(net)
;
sql/client.c:cli_advanced_command
???net_clear(&mysql->net,?(command?!=?COM_QUIT))
;

開(kāi)始兩方net->pkt_nr皆為0, 命令發(fā)送后客戶(hù)端端為1,服務(wù)端開(kāi)始發(fā)送分包,分包的pkt_nr的依次遞增,客戶(hù)端的net->pkt_nr也隨之增加.

?c?——0—–>?s?0??query
?c?<—-1——s?2??resultset
?c?<—-2——s?3??resultset

解包的細(xì)節(jié)

my_net_read負(fù)責(zé)解包,首先讀取4個(gè)字節(jié),判斷packet number是否等于net->pkt_nr然后再次讀取packet_number長(zhǎng)度的包體。

偽代碼如下:

remain=4
for(i?=?0;?i?2;?i++)?{
????//數(shù)據(jù)是否讀完
????while?(remain>0)??{
????????length?=?read(fd,?net->buff,?remain)
????????remain?=?remain?-?length
????}
????//第一次
????if?(i=0)?{
????????remain?=?uint3korr(net->buff+net->where_b);
????}
}

網(wǎng)絡(luò)層優(yōu)化

從ppt里可以看到,一個(gè)resultset packet由多個(gè)包組成,如果每次讀寫(xiě)包都導(dǎo)致系統(tǒng)調(diào)用那肯定是不合理,常規(guī)優(yōu)化方法:寫(xiě)大包加預(yù)讀

NET->BUFF

每個(gè)包發(fā)送到網(wǎng)絡(luò)或者從網(wǎng)絡(luò)讀包都會(huì)先把數(shù)據(jù)包保存在net->buff里,待到net->buff滿(mǎn)了或者一次命令結(jié)束才會(huì)通過(guò)socket發(fā)出給對(duì)端.net->buff有個(gè)初始大小(net->max_packet),會(huì)隨讀取數(shù)據(jù)的增多而擴(kuò)展.

VIO->READ_BUFFER

每次從網(wǎng)絡(luò)讀包,并不是按包的大小讀取,而是會(huì)盡量讀取2048個(gè)字節(jié),這樣一個(gè)resultset包的讀取不會(huì)再引起多次的系統(tǒng)調(diào)用了.header packet讀取完畢后, 接下來(lái)的field,eof, row ?apcket讀取僅僅需要從vio-read_buffer拷貝指定字節(jié)的數(shù)據(jù)即可.

MYSQL API說(shuō)明

api和MySQL客戶(hù)端都會(huì)使用sql/client.c這個(gè)文件,解包的過(guò)程都是使用sql/client.c:cli_read_query_result.

mysql_store_result來(lái)解析row packet,并把數(shù)據(jù)存儲(chǔ)到res->data里,此時(shí)所有數(shù)據(jù)都存內(nèi)存里了.

mysql_fetch_row僅僅是使用內(nèi)部的游標(biāo),遍歷result->data里的數(shù)據(jù)

if?(!res->data_cursor)
{
????DBUG_PRINT("info",("end?of?data"));
????DBUG_RETURN(res->current_row=(MYSQL_ROW)?NULL);
}
tmp?=?res->data_cursor->data;
res->data_cursor?=?res->data_cursor->next;
DBUG_RETURN(res->current_row=tmp);

mysql_free_result是把result->data指定的行數(shù)據(jù)釋放掉.

大部分參考:http://hutaow.com/blog/2013/11/06/mysql-protocol-analysis/

特別推薦一個(gè)分享架構(gòu)+算法的優(yōu)質(zhì)內(nèi)容,還沒(méi)關(guān)注的小伙伴,可以長(zhǎng)按關(guān)注一下:

怒肝兩個(gè)月MySQL源碼,總結(jié)出這篇2W字的MySQL協(xié)議詳解

怒肝兩個(gè)月MySQL源碼,總結(jié)出這篇2W字的MySQL協(xié)議詳解

怒肝兩個(gè)月MySQL源碼,總結(jié)出這篇2W字的MySQL協(xié)議詳解

長(zhǎng)按訂閱更多精彩▼

怒肝兩個(gè)月MySQL源碼,總結(jié)出這篇2W字的MySQL協(xié)議詳解

如有收獲,點(diǎn)個(gè)在看,誠(chéng)摯感謝

免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專(zhuān)欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除( 郵箱:macysun@21ic.com )。
換一批
延伸閱讀
關(guān)閉