日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

mysql long类型_怒肝两个月MySQL源码,我总结出这篇2W字的MySQL协议详解(超硬核干货)!!...

發布時間:2023/12/10 数据库 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 mysql long类型_怒肝两个月MySQL源码,我总结出这篇2W字的MySQL协议详解(超硬核干货)!!... 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

點擊上方藍色“冰河技術”,關注并選擇“設為星標”

持之以恒,貴在堅持,每天進步一點點!

作者個人研發的在高并發場景下,提供的簡單、穩定、可擴展的延遲消息隊列框架,具有精準的定時任務和延遲隊列處理功能。自開源半年多以來,已成功為十幾家中小型企業提供了精準定時調度方案,經受住了生產環境的考驗。為使更多童鞋受益,現給出開源框架地址:

https://github.com/sunshinelyz/mykit-delay

PS: 歡迎各位Star源碼,也可以pr你牛逼哄哄的代碼。? ? ??

寫在前面

最近,在開發一個分庫分表中間件,由于功能需求,需要分析MySQL協議,發現網上對于MySQL協議分析的文章大部分都過時了,原因是分析的MySQL版本太低了。怎么辦呢?于是乎,我便硬著頭皮開始啃MySQL源碼,經過兩個多月的整理,終于總結出這篇MySQL協議。

注:部分來自于互聯網,感謝數據庫大牛前輩們的默默付出!!

交互過程

MySQL客戶端與服務器的交互主要分為兩個階段:握手認證階段和命令執行階段。

握手認證階段

握手認證階段為客戶端與服務器建立連接后進行,交互過程如下:

  • 服務器 -> 客戶端:握手初始化消息
  • 客戶端 -> 服務器:登陸認證消息
  • 服務器 -> 客戶端:認證結果消息

命令執行階段

客戶端認證成功后,會進入命令執行階段,交互過程如下:

  • 客戶端 -> 服務器:執行命令消息
  • 服務器 -> 客戶端:命令執行結果

MySQL客戶端與服務器的完整交互過程如下

基本類型

整型值

MySQL報文中整型值分別有1、2、3、4、8字節長度,使用小字節序傳輸。

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

字符串長度不固定,當遇到'NULL'(0x00)字符時結束。

二進制數據(長度編碼)(Length Coded Binary)

數據長度不固定,長度值由數據前的1-9個字節決定,其中長度值所占的字節數不定,字節數由第1個字節決定,如下表:

第一個字節值后續字節數長度值說明
0-2500第一個字節值即為數據的真實長度
2510空數據,數據的真實長度為零
2522后續額外2個字節標識了數據的真實長度
2533后續額外3個字節標識了數據的真實長度
2548后續額外8個字節標識了數據的真實長度

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

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

報文結構

報文分為消息頭和消息體兩部分,其中消息頭占用固定的4個字節,消息體長度由消息頭中的長度字段決定,報文結構如下:

消息頭

報文長度

用于標記當前請求消息的實際數據長度值,以字節為單位,占用3個字節,最大值為 0xFFFFFF,即接近 16 MB 大小(比16MB少1個字節)。

序號

在一次完整的請求/響應交互過程中,用于保證消息順序的正確,每次客戶端發起請求時,序號值都會從0開始計算。

消息體

消息體用于存放請求的內容及響應的數據,長度由消息頭中的長度值決定。

報文類型

登陸認證交互報文

握手初始化報文(服務器 -> 客戶端)

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

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

服務器線程ID:服務器為當前連接所創建的線程ID。

挑戰隨機數:MySQL數據庫用戶認證采用的是挑戰/應答的方式,服務器生成該挑戰數并發送給客戶端,由客戶端進行處理并返回相應結果,然后服務器檢查是否與預期的結果相同,從而完成用戶認證的過程。

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

標志位名稱標志位說明
CLIENT_LONG_PASSWORD0x0001new more secure passwords
CLIENT_FOUND_ROWS0x0002Found instead of affected rows
CLIENT_LONG_FLAG0x0004Get all column flags
CLIENT_CONNECT_WITH_DB0x0008One can specify db on connect
CLIENT_NO_SCHEMA0x0010Do not allow database.table.column
CLIENT_COMPRESS0x0020Can use compression protocol
CLIENT_ODBC0x0040Odbc client
CLIENT_LOCAL_FILES0x0080Can use LOAD DATA LOCAL
CLIENT_IGNORE_SPACE0x0100Ignore spaces before '('
CLIENT_PROTOCOL_410x0200New 4.1 protocol
CLIENT_INTERACTIVE0x0400This is an interactive client
CLIENT_SSL0x0800Switch to SSL after handshake
CLIENT_IGNORE_SIGPIPE0x1000IGNORE sigpipes
CLIENT_TRANSACTIONS0x2000Client knows about transactions
CLIENT_RESERVED0x4000Old flag for 4.1 protocol
CLIENT_SECURE_CONNECTION0x8000New 4.1 authentication
CLIENT_MULTI_STATEMENTS0x0001 0000Enable/disable multi-stmt support
CLIENT_MULTI_RESULTS0x0002 0000Enable/disable multi-results

字符編碼:標識服務器所使用的字符集。

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

狀態名稱狀態值
SERVER_STATUS_IN_TRANS0x0001
SERVER_STATUS_AUTOCOMMIT0x0002
SERVER_STATUS_CURSOR_EXISTS0x0040
SERVER_STATUS_LAST_ROW_SENT0x0080
SERVER_STATUS_DB_DROPPED0x0100
SERVER_STATUS_NO_BACKSLASH_ESCAPES0x0200
SERVER_STATUS_METADATA_CHANGED0x0400

登陸認證報文(客戶端 -> 服務器)

MySQL 4.0 及之前的版本

MySQL 4.1 及之后的版本

客戶端權能標志:用于與客戶端協商通訊方式,標志位含義與握手初始化報文中的相同。客戶端收到服務器發來的初始化報文后,會對服務器發送的權能標志進行修改,保留自身所支持的功能,然后將權能標返回給服務器,從而保證服務器與客戶端通訊的兼容性。

最大消息長度:客戶端發送請求報文時所支持的最大消息長度值。

字符編碼:標識通訊過程中使用的字符編碼,與服務器在認證初始化報文中發送的相同。

用戶名:客戶端登陸用戶的用戶名稱。

挑戰認證數據:客戶端用戶密碼使用服務器發送的挑戰隨機數進行加密后,生成挑戰認證數據,然后返回給服務器,用于對用戶身份的認證。

數據庫名稱:當客戶端的權能標志位 CLIENT_CONNECT_WITH_DB 被置位時,該字段必須出現。

客戶端命令請求報文(客戶端 -> 服務器)

命令:用于標識當前請求消息的類型,例如切換數據庫(0x02)、查詢命令(0x03)等。命令值的取值范圍及說明如下表(參考MySQL源代碼/include/mysql_com.h頭文件中的定義):

類型值命令功能關聯函數
0x00COM_SLEEP(內部線程狀態)(無)
0x01COM_QUIT關閉連接mysql_close
0x02COM_INIT_DB切換數據庫mysql_select_db
0x03COM_QUERYSQL查詢請求mysql_real_query
0x04COM_FIELD_LIST獲取數據表字段信息mysql_list_fields
0x05COM_CREATE_DB創建數據庫mysql_create_db
0x06COM_DROP_DB刪除數據庫mysql_drop_db
0x07COM_REFRESH清除緩存mysql_refresh
0x08COM_SHUTDOWN停止服務器mysql_shutdown
0x09COM_STATISTICS獲取服務器統計信息mysql_stat
0x0ACOM_PROCESS_INFO獲取當前連接的列表mysql_list_processes
0x0BCOM_CONNECT(內部線程狀態)(無)
0x0CCOM_PROCESS_KILL中斷某個連接mysql_kill
0x0DCOM_DEBUG保存服務器調試信息mysql_dump_debug_info
0x0ECOM_PING測試連通性mysql_ping
0x0FCOM_TIME(內部線程狀態)(無)
0x10COM_DELAYED_INSERT(內部線程狀態)(無)
0x11COM_CHANGE_USER重新登陸(不斷連接)mysql_change_user
0x12COM_BINLOG_DUMP獲取二進制日志信息(無)
0x13COM_TABLE_DUMP獲取數據表結構信息(無)
0x14COM_CONNECT_OUT(內部線程狀態)(無)
0x15COM_REGISTER_SLAVE從服務器向主服務器進行注冊(無)
0x16COM_STMT_PREPARE預處理SQL語句mysql_stmt_prepare
0x17COM_STMT_EXECUTE執行預處理語句mysql_stmt_execute
0x18COM_STMT_SEND_LONG_DATA發送BLOB類型的數據mysql_stmt_send_long_data
0x19COM_STMT_CLOSE銷毀預處理語句mysql_stmt_close
0x1ACOM_STMT_RESET清除預處理語句參數緩存mysql_stmt_reset
0x1BCOM_SET_OPTION設置語句選項mysql_set_server_option
0x1CCOM_STMT_FETCH獲取預處理語句的執行結果mysql_stmt_fetch

參數:內容是用戶在MySQL客戶端輸入的命令(不包括每行命令結尾的";"分號)。另外這個字段的字符串不是以NULL字符結尾,而是通過消息頭中的長度值計算而來。

例如:當我們在MySQL客戶端中執行use hutaow;命令時(切換到hutaow數據庫),發送的請求報文數據會是下面的樣子:

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

其中,0x02為請求類型值COM_INIT_DB,后面的0x68 0x75 0x74 0x61 0x6f 0x77為ASCII字符hutaow。

COM_QUIT 消息報文

功能:關閉當前連接(客戶端退出),無參數。

COM_INIT_DB 消息報文

功能:切換數據庫,對應的SQL語句為USE。

字節說明
n數據庫名稱(字符串到達消息尾部時結束,無結束符)

COM_QUERY 消息報文

功能:最常見的請求消息類型,當用戶執行SQL語句時發送該消息。

字節說明
nSQL語句(字符串到達消息尾部時結束,無結束符)

COM_FIELD_LIST 消息報文

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

字節說明
n表格名稱(Null-Terminated String)
n字段(列)名稱或通配符(可選)

COM_CREATE_DB 消息報文

功能:創建數據庫,該消息已過時,而被SQL語句CREATE DATABASE代替。

字節說明
n數據庫名稱(字符串到達消息尾部時結束,無結束符)

COM_DROP_DB 消息報文

功能:刪除數據庫,該消息已過時,而被SQL語句DROP DATABASE代替。

字節說明
n數據庫名稱(字符串到達消息尾部時結束,無結束符)

COM_REFRESH 消息報文

功能:清除緩存,等同于SQL語句FLUSH,或是執行mysqladmin flush-foo命令時發送該消息。

字節說明
1清除緩存選項(位圖方式存儲,各標志位含義如下)
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 消息報文

功能:停止MySQL服務。執行mysqladmin shutdown命令時發送該消息。

字節說明
1停止服務選項
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 消息報文

功能:查看MySQL服務的統計信息(例如運行時間、每秒查詢次數等)。執行mysqladmin status命令時發送該消息,無參數。

COM_PROCESS_INFO 消息報文

功能:獲取當前活動的線程(連接)列表。等同于SQL語句SHOW PROCESSLIST,或是執行mysqladmin processlist命令時發送該消息,無參數。

COM_PROCESS_KILL 消息報文

功能:要求服務器中斷某個連接。等同于SQL語句KILL。

字節說明
4連接ID號(小字節序)

COM_DEBUG 消息報文

功能:要求服務器將調試信息保存下來,保存的信息多少依賴于編譯選項設置(debug=no|yes|full)。執行mysqladmin debug命令時發送該消息,無參數。

COM_PING 消息報文

功能:該消息用來測試連通性,同時會將服務器的無效連接(超時)計數器清零。執行mysqladmin ping命令時發送該消息,無參數。

COM_CHANGE_USER 消息報文

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

字節說明
n用戶名(字符串以NULL結尾)
n密碼(挑戰數)
MySQL 3.23 版本:Null-Terminated String(長度9字節)
MySQL 4.1 版本:Length Coded String(長度1+21字節)
n數據庫名稱(Null-Terminated String)
2字符編碼

COM_BINLOG_DUMP 消息報文

功能:該消息是備份連接時由從服務器向主服務器發送的最后一個請求,主服務器收到后,會響應一系列的報文,每個報文都包含一個二進制日志事件。如果主服務器出現故障時,會發送一個EOF報文。

字節說明
4二進制日志數據的起始位置(小字節序)
4二進制日志數據標志位(目前未使用,永遠為0x00)
4從服務器的服務器ID值(小字節序)
n二進制日志的文件名稱(可選,默認值為主服務器上第一個有效的文件名)

COM_TABLE_DUMP 消息報文

功能:將數據表從主服務器復制到從服務器中,執行SQL語句LOAD TABLE ... FROM MASTER時發送該消息。目前該消息已過時,不再使用。

字節說明
n數據庫名稱(Length Coded String)
n數據表名稱(Length Coded String)

COM_REGISTER_SLAVE 消息報文

功能:在從服務器report_host變量設置的情況下,當備份連接時向主服務器發送的注冊消息。

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

COM_PREPARE 消息報文

功能:預處理SQL語句,使用帶有"?"占位符的SQL語句時發送該消息。

字節說明
n帶有"?"占位符的SQL語句(字符串到達消息尾部時結束,無結束符)

COM_EXECUTE 消息報文

功能:執行預處理語句。

字節說明
4預處理語句的ID值
1標志位
0x00: CURSOR_TYPE_NO_CURSOR
0x01: CURSOR_TYPE_READ_ONLY
0x02: CURSOR_TYPE_FOR_UPDATE
0x04: CURSOR_TYPE_SCROLLABLE
4保留(值恒為0x01)
如果參數數量大于0
n空位圖(Null-Bitmap,長度 = (參數數量 + 7) / 8 字節)
1參數分隔標志
如果參數分隔標志值為1
n每個參數的類型值(長度 = 參數數量 * 2 字節)
n每個參數的值

COM_LONG_DATA 消息報文

該消息報文有兩種形式,一種用于發送二進制數據,另一種用于發送文本數據。

功能:用于發送二進制(BLOB)類型的數據(調用mysql_stmt_send_long_data函數)。

字節說明
4預處理語句的ID值(小字節序)
2參數序號(小字節序)
n數據負載(數據到達消息尾部時結束,無結束符)

功能:用于發送超長字符串類型的數據(調用mysql_send_long_data函數)

字節說明
4預處理語句的ID值(小字節序)
2參數序號(小字節序)
2數據類型(未使用)
n數據負載(數據到達消息尾部時結束,無結束符)

COM_CLOSE_STMT 消息報文

功能:銷毀預處理語句。

字節說明
4預處理語句的ID值(小字節序)

COM_RESET_STMT 消息報文

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

字節說明
4預處理語句的ID值(小字節序)

COM_SET_OPTION 消息報文

功能:設置語句選項,選項值為/include/mysql_com.h頭文件中定義的enum_mysql_set_option枚舉類型:

  • MYSQL_OPTION_MULTI_STATEMENTS_ON
  • MYSQL_OPTION_MULTI_STATEMENTS_OFF
字節說明
2選項值(小字節序)

COM_FETCH_STMT 消息報文

功能:獲取預處理語句的執行結果(一次可以獲取多行數據)。

字節說明
4預處理語句的ID值(小字節序)
4數據的行數(小字節序)

服務器響應報文(服務器 -> 客戶端)

當客戶端發起認證請求或命令請求后,服務器會返回相應的執行結果給客戶端。客戶端在收到響應報文后,需要首先檢查第1個字節的值,來區分響應報文的類型。

響應報文類型第1個字節取值范圍
OK 響應報文0x00
Error 響應報文0xFF
Result Set 報文0x01 - 0xFA
Field 報文0x01 - 0xFA
Row Data 報文0x01 - 0xFA
EOF 報文0xFE

注:響應報文的第1個字節在不同類型中含義不同,比如在OK報文中,該字節并沒有實際意義,值恒為0x00;而在Result Set報文中,該字節又是長度編碼的二進制數據結構(Length Coded Binary)中的第1字節。

響應報文

客戶端的命令執行正確時,服務器會返回OK響應報文。

MySQL 4.0 及之前的版本

字節說明
1OK報文,值恒為0x00
1-9受影響行數(Length Coded Binary)
1-9索引ID值(Length Coded Binary)
2服務器狀態
n服務器消息(字符串到達消息尾部時結束,無結束符)

MySQL 4.1 及之后的版本

字節說明
1OK報文,值恒為0x00
1-9受影響行數(Length Coded Binary)
1-9索引ID值(Length Coded Binary)
2服務器狀態
2告警計數
n服務器消息(字符串到達消息尾部時結束,無結束符,可選)

受影響行數:當執行INSERT/UPDATE/DELETE語句時所影響的數據行數。

索引ID值:該值為AUTO_INCREMENT索引字段生成,如果沒有索引字段,則為0x00。注意:當INSERT插入語句為多行數據時,該索引ID值為第一個插入的數據行索引值,而非最后一個。

服務器狀態:客戶端可以通過該值檢查命令是否在事務處理中。

告警計數:告警發生的次數。

服務器消息:服務器返回給客戶端的消息,一般為簡單的描述性字符串,可選字段。

響應報文

MySQL 4.0 及之前的版本

字節說明
1Error報文,值恒為0xFF
2錯誤編號(小字節序)
n服務器消息

MySQL 4.1 及之后的版本

字節說明
1Error報文,值恒為0xFF
2錯誤編號(小字節序)
1服務器狀態標志,恒為'#'字符
5服務器狀態(5個字符)
n服務器消息

錯誤編號:錯誤編號值定義在源代碼/include/mysqld_error.h頭文件中。

服務器狀態:服務器將錯誤編號通過mysql_errno_to_sqlstate函數轉換為狀態值,狀態值由5字節的ASCII字符組成,定義在源代碼/include/sql_state.h頭文件中。

服務器消息:錯誤消息字符串到達消息尾時結束,長度可以由消息頭中的長度值計算得出。消息長度為0-512字節。

Result Set 消息

當客戶端發送查詢請求后,在沒有錯誤的情況下,服務器會返回結果集(Result Set)給客戶端。

Result Set 消息分為五部分,結構如下:

結構說明
[Result Set Header]列數量
[Field]列信息(多個)
[EOF]列結束
[Row Data]行數據(多個)
[EOF]數據結束

Result Set Header 結構

字節說明
1-9Field結構計數(Length Coded Binary)
1-9額外信息(Length Coded Binary)

Field結構計數:用于標識Field結構的數量,取值范圍0x00-0xFA。

額外信息:可選字段,一般情況下不應該出現。只有像SHOW COLUMNS這種語句的執行結果才會用到額外信息(標識表格的列數量)。

Field 結構

Field為數據表的列信息,在Result Set中,Field會連續出現多次,次數由Result Set Header結構中的IField結構計數值決定。

MySQL 4.0 及之前的版本

字節說明
n數據表名稱(Length Coded String)
n列(字段)名稱(Length Coded String)
4列(字段)長度(Length Coded String)
2列(字段)類型(Length Coded String)
2列(字段)標志(Length Coded String)
1整型值精度
n默認值(Length Coded String)

MySQL 4.1 及之后的版本

字節說明
n目錄名稱(Length Coded String)
n數據庫名稱(Length Coded String)
n數據表名稱(Length Coded String)
n數據表原始名稱(Length Coded String)
n列(字段)名稱(Length Coded String)
4列(字段)原始名稱(Length Coded String)
1填充值
2字符編碼
4列(字段)長度
1列(字段)類型
2列(字段)標志
1整型值精度
2填充值(0x00)
n默認值(Length Coded String)

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

數據庫名稱:數據庫名稱標識。

數據表名稱:數據表的別名(AS之后的名稱)。

數據表原始名稱:數據表的原始名稱(AS之前的名稱)。

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

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

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

列(字段)長度:列(字段)的長度值,真實長度可能小于該值,例如VARCHAR(2)類型的字段實際只能存儲1個字符。

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

類型值名稱
0x00FIELD_TYPE_DECIMAL
0x01FIELD_TYPE_TINY
0x02FIELD_TYPE_SHORT
0x03FIELD_TYPE_LONG
0x04FIELD_TYPE_FLOAT
0x05FIELD_TYPE_DOUBLE
0x06FIELD_TYPE_NULL
0x07FIELD_TYPE_TIMESTAMP
0x08FIELD_TYPE_LONGLONG
0x09FIELD_TYPE_INT24
0x0AFIELD_TYPE_DATE
0x0BFIELD_TYPE_TIME
0x0CFIELD_TYPE_DATETIME
0x0DFIELD_TYPE_YEAR
0x0EFIELD_TYPE_NEWDATE
0x0FFIELD_TYPE_VARCHAR (new in MySQL 5.0)
0x10FIELD_TYPE_BIT (new in MySQL 5.0)
0xF6FIELD_TYPE_NEWDECIMAL (new in MYSQL 5.0)
0xF7FIELD_TYPE_ENUM
0xF8FIELD_TYPE_SET
0xF9FIELD_TYPE_TINY_BLOB
0xFAFIELD_TYPE_MEDIUM_BLOB
0xFBFIELD_TYPE_LONG_BLOB
0xFCFIELD_TYPE_BLOB
0xFDFIELD_TYPE_VAR_STRING
0xFEFIELD_TYPE_STRING
0xFFFIELD_TYPE_GEOMETRY

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

標志位名稱
0x0001NOT_NULL_FLAG
0x0002PRI_KEY_FLAG
0x0004UNIQUE_KEY_FLAG
0x0008MULTIPLE_KEY_FLAG
0x0010BLOB_FLAG
0x0020UNSIGNED_FLAG
0x0040ZEROFILL_FLAG
0x0080BINARY_FLAG
0x0100ENUM_FLAG
0x0200AUTO_INCREMENT_FLAG
0x0400TIMESTAMP_FLAG
0x0800SET_FLAG

數值精度:該字段對DECIMAL和NUMERIC類型的數值字段有效,用于標識數值的精度(小數點位置)。

默認值:該字段用在數據表定義中,普通的查詢結果中不會出現。

:Field結構的相關處理函數:

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

EOF 結構

EOF結構用于標識Field和Row Data的結束,在預處理語句中,EOF也被用來標識參數的結束。

MySQL 4.0 及之前的版本

字節說明
1EOF值(0xFE)

MySQL 4.1 及之后的版本

字節說明
1EOF值(0xFE)
2告警計數
2狀態標志位

告警計數:服務器告警數量,在所有數據都發送給客戶端后該值才有效。

狀態標志位:包含類似SERVER_MORE_RESULTS_EXISTS這樣的標志位。

:由于EOF值與其它Result Set結構共用1字節,所以在收到報文后需要對EOF包的真實性進行校驗,校驗條件為:

  • 第1字節值為0xFE
  • 包長度小于9字節

:EOF結構的相關處理函數:

  • 服務器:protocol.cc源文件中的send_eof函數

Row Data 結構

在Result Set消息中,會包含多個Row Data結構,每個Row Data結構又包含多個字段值,這些字段值組成一行數據。

字節說明
n字段值(Length Coded String)
...(一行數據中包含多個字段值)

字段值:行數據中的字段值,字符串形式。

:Row Data結構的相關處理函數:

  • 客戶端:/client/client.c源文件中的read_rows函數

Row Data 結構(二進制數據)

該結構用于傳輸二進制的字段值,既可以是服務器返回的結果,也可以是由客戶端發送的(當執行預處理語句時,客戶端使用Result Set消息來發送參數及數據)。

字節說明
1結構頭(0x00)
(列數量 + 7 + 2) / 8空位圖
n字段值
...(一行數據中包含多個字段值)

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

字段值:行數據中的字段值,二進制形式。

PREPARE_OK 響應報文(Prepared Statement)

用于響應客戶端發起的預處理語句報文,組成結構如下:

結構說明
[PREPARE_OK]PREPARE_OK結構
如果參數數量大于0
[Field]與Result Set消息結構相同
[EOF]
如果列數大于0
[Field]與Result Set消息結構相同
[EOF]

其中 PREPARD_OK 的結構如下:

字節說明
1OK報文,值為0x00
4預處理語句ID值
2列數量
2參數數量
1填充值(0x00)
2告警計數

Parameter 響應報文(Prepared Statement)

預處理語句的值與參數正確對應后,服務器會返回 Parameter 報文。

字節說明
2類型
2標志
1數值精度
4字段長度

類型:與 Field 結構中的字段類型相同。

標志:與 Field 結構中的字段標志相同。

數值精度:與 Field 結構中的數值精度相同。

字段長度:與 Field 結構中的字段長度相同。

代碼分析

議程
協議頭
協議類型?網絡協議相關函數?NET緩沖?VIO緩沖?MySQL?API

協議頭

● 數據變成在網絡里傳輸的數據,需要額外的在頭部添加4 個字節的包頭.

. packet length(3字節), 包體的長度

. packet number(1字節), 從0開始的遞增的

● sql “select 1” 的網絡協議是?

協議頭

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

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

協議類型

● handshake

● auth

● ok|error

● resultset

○ header

○ field

○ eof

○ row

● command packet

連接時的交互

協議說明

● 協議內字段分三種形式

○ 固定長度(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, 字符串的長度保存在字符串的前面

■ length<251 1 byte

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

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

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

handshake packet

● 該協議由服務端發送客戶端

● 括號內為字節數,字節數為n為是null-terminated string;字節數為大寫的N表示length code binary.

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

● sql_connect.cc:check_connection

● sql_client.c:mysql_real_connect

auth packet

● 該協議是從客戶端對密碼使用scramble加密后發送到服務端

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

● sql_client.c:mysql_real_connect

● sql_connect.c:check_connection

ok packet

● ok包,命令和insert,update,delete的返回結果

● 包體首字節為0.

● insert_id, affect_rows也是一并發過來.

● src/protocol.cc:net_send_ok

error packet

● 錯誤的命令,非法的sql的返回包

● 包體首字節為255.

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

● sqlstate是錯誤狀態,include/sql_state.h

● message是錯誤的信息

● sql/protocol.cc:net_send_error_packet

resultset packet

● 結果集的數據包,由多個packet組合而成

● 例如查詢一個結構集,順序如下: ○ header ○ field1....fieldN ○ eof ○ row1...rowN ○ eof

● sql/client.c:cli_read_query_result

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

resultset packet - header

● field packet number決定了接下來的field packet的個數.

● 一個返回6行記錄,3個字段的查詢語句

resultset packet - field

● 結果集中一個字段一個field packet.

● tables_alias是sql語句里表的別名,org_table才是表的真 實名字.

● sql/protocol.cc:Protocol::send_fields

● sql/client.c:cli_read_query_result

resultset packet - eof

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

● 包體首字節為254

● sql/protocol.cc:net_send_eof

resultset packet - row

● row packet里才是真正的數據包.一行數據一個packet.

● row里的每個字段都是length coded binary

● 字段的個數在header packet里

● sql/client.c:cli_read_rows

command packet

● 命令包,包括我們的sql語句還有一些常見的命令.

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

網絡協議關鍵函數

● net_write_command(sql/net_serv.cc)所有的sql最終調用這個命令發送出去.

● my_net_write(sql/net_serv.cc)連接階段的socket write操作調用這個函數.

● my_net_read讀取包,會判斷包大小,是否是分包

● my_real_read解析MySQL packet,第一次讀取4字節,根據packet length再讀取余下來的長度

● cli_safe_read客戶端解包函數,包含了my_net_read

NET緩沖

● 每次socket操作都會先把數據寫,讀到net->buff,這是一 個緩沖區, 減少系統調用調用的次數.

● 當寫入的數據和buff內的數據超過buff大小才會發出一次 write操作,然后再把要寫入的buff里插入數, 寫入不會 導致buff區區域擴展.(sql/net_serv.cc: net_write_buff).

● net->buff大小初始net->max_packet, 讀取會導致會導致 buff的realloc最大net->max_packet_size

● 一次sql命令的結束都會調用net_flush,把buff里的數據 都寫到socket里.

VIO緩沖

● 從my_read_read可以看出每次packet讀取都是按需讀取, 為了減少系統調用,vio層面加了一個read_buffer.

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

MySQL API

● 數據從mysql_send_query處發送給服務端,實際調用的是 net_write_command.

● cli_read_query_result解析header packet, field packet,獲 得field_count的個數

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

● myql_fetch_row其實遍歷result->data

PACKET NUMBER

在做proxy的時候在這里迷糊過,翻了幾遍代碼才搞明白,細節如下:??客戶端服務端的net->pkt_nr都從0開始.接受包時比較packet number ?和net->pkt_nr是否相等,否則報packet number亂序,連接報錯;相等則pkt_nr自增.發送包時把net->pkt_nr作為packet number發送,然后對net->pkt_nr進行自增保持和對端的同步.

接收包

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

發送包

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

我們來幾個具體場景的packet number, net->pkt_nr的變化

連接

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

開始兩方都為0,服務端發送handshake packet(pkt=0)之后自增為1,然后等待對端發送過來pkt=1的包

查詢

每次查詢,服務客戶端都會對net->pkt_nr進行清零

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

開始兩方net->pkt_nr皆為0, 命令發送后客戶端端為1,服務端開始發送分包,分包的pkt_nr的依次遞增,客戶端的net->pkt_nr也隨之增加.

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

解包的細節

my_net_read負責解包,首先讀取4個字節,判斷packet number是否等于net->pkt_nr然后再次讀取packet_number長度的包體。

偽代碼如下:

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

網絡層優化

從ppt里可以看到,一個resultset packet由多個包組成,如果每次讀寫包都導致系統調用那肯定是不合理,常規優化方法:寫大包加預讀

NET->BUFF

每個包發送到網絡或者從網絡讀包都會先把數據包保存在net->buff里,待到net->buff滿了或者一次命令結束才會通過socket發出給對端.net->buff有個初始大小(net->max_packet),會隨讀取數據的增多而擴展.

VIO->READ_BUFFER

每次從網絡讀包,并不是按包的大小讀取,而是會盡量讀取2048個字節,這樣一個resultset包的讀取不會再引起多次的系統調用了.header packet讀取完畢后, 接下來的field,eof, row ?apcket讀取僅僅需要從vio-read_buffer拷貝指定字節的數據即可.

MYSQL API說明

api和MySQL客戶端都會使用sql/client.c這個文件,解包的過程都是使用sql/client.c:cli_read_query_result.

mysql_store_result來解析row packet,并把數據存儲到res->data里,此時所有數據都存內存里了.

mysql_fetch_row僅僅是使用內部的游標,遍歷result->data里的數據

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指定的行數據釋放掉.

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

重磅福利

微信搜一搜【冰河技術】微信公眾號,關注這個有深度的程序員,每天閱讀超硬核技術干貨,公眾號內回復【PDF】有我準備的一線大廠面試資料和我原創的超硬核PDF技術文檔,以及我為大家精心準備的多套簡歷模板(不斷更新中),希望大家都能找到心儀的工作,學習是一條時而郁郁寡歡,時而開懷大笑的路,加油。如果你通過努力成功進入到了心儀的公司,一定不要懈怠放松,職場成長和新技術學習一樣,不進則退。如果有幸我們江湖再見!

另外,我開源的各個PDF,后續我都會持續更新和維護,感謝大家長期以來對冰河的支持!!

寫在最后

如果你覺得冰河寫的還不錯,請微信搜索并關注「?冰河技術?」微信公眾號,跟冰河學習高并發、分布式、微服務、大數據、互聯網和云原生技術,「?冰河技術?」微信公眾號更新了大量技術專題,每一篇技術文章干貨滿滿!不少讀者已經通過閱讀「?冰河技術?」微信公眾號文章,吊打面試官,成功跳槽到大廠;也有不少讀者實現了技術上的飛躍,成為公司的技術骨干!如果你也想像他們一樣提升自己的能力,實現技術能力的飛躍,進大廠,升職加薪,那就關注「?冰河技術?」微信公眾號吧,每天更新超硬核技術干貨,讓你對如何提升技術能力不再迷茫!

留言區

總結

以上是生活随笔為你收集整理的mysql long类型_怒肝两个月MySQL源码,我总结出这篇2W字的MySQL协议详解(超硬核干货)!!...的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 亚洲 欧美 日韩 国产综合 在线 | 亚洲综合色在线观看 | 8x8x永久免费视频 | 国产亚洲制服欧洲高清一区 | 日本中文字幕一区 | 91亚洲精品国偷拍自产在线观看 | 国产无遮无挡120秒 欧美综合图片 | 五月婷婷国产 | 内地毛片| 成人在线激情网 | 宅男噜噜噜66一区二区 | 日韩av资源 | 打开每日更新在线观看 | 精品成人免费一区二区在线播放 | 国产毛片久久久久久国产毛片 | 日韩欧美不卡在线 | 国产午夜亚洲精品午夜鲁丝片 | 日本视频免费在线播放 | 色男人的天堂 | 欧美丰满少妇 | a级片在线观看 | 国产精品人成在线观看免费 | 色接久久 | 亚洲av永久无码精品国产精品 | 丁香婷婷激情五月 | 在线成人播放 | 日本精品一区二区三区视频 | 亚洲国产精品国自产拍av | 91视频色 | 日日夜夜撸撸 | 国产亚洲综合在线 | 懂色一区二区三区免费观看 | 少妇诱惑av| 男人插女人的网站 | 华人在线视频 | 日韩三级视频 | 少妇久久久久久被弄高潮 | 国产网站在线看 | 99久久99久久精品国产片果冰 | 国产无遮挡一区二区三区毛片日本 | 成人福利网站在线观看 | 国产精品久久久久9999 | 伊人青青操 | 欧美大片在线播放 | 波多野结衣av在线免费观看 | 日韩专区在线 | 丁香av| 久久久久久18| 国产亚洲综合精品 | 全黄一级裸片视频 | 亚洲av色香蕉一区二区三区 | 久久久午夜精品福利内容 | 国产精品入口66mio男同 | 精品人妻天天爽夜夜爽视频 | 黄瓜视频在线播放 | 伊人久久影视 | 成年人毛片视频 | 午夜视频在线观看一区二区 | 少妇高潮一区二区三区在线 | 日韩三级在线播放 | 欧美日皮视频 | 亚洲图片欧美色图 | 伊人福利在线 | 草逼视频免费看 | 亚洲欧美一区二区精品久久久 | av地址在线观看 | 玉女心经在线看 | 国产制服在线 | 欧美成人免费在线观看视频 | 快播视频在线观看 | 久久精品国产精品亚洲 | 三大队在线观看 | 人人干免费 | 9191国产精品 | 成人毛片100部免费看 | 成人羞羞国产免费 | 亚洲青草视频 | 黄色一级视频片 | 深夜福利av| 刘亦菲毛片一区二区三区 | 国产主播在线播放 | 久久国产美女视频 | 精品人妻一区二区三区麻豆91 | 青娱乐在线视频观看 | 成人免费看片' | 精品熟妇视频一区二区三区 | 亚洲大尺度在线观看 | 我和我的太阳泰剧在线观看泰剧 | av高清不卡 | 1024精品一区二区三区日韩 | www.国产精品 | 国产乱子伦精品 | 日本寂寞少妇 | 日日摸夜夜添夜夜添高潮喷水 | 久久综合91| 91精品欧美一区二区三区 | 日韩精品人妻无码一本 | 久久美女免费视频 | 中文字幕日韩视频 |