壓縮協議屬於mysql通訊協議的一部分,要啟用壓縮協議傳輸功能,前提條件客戶端和服務端都必須要支持zlib算法,那么,現在有個問題,假如服務端已經默認開啟壓縮功能,那原生客戶端在連接的時候要如何才可啟用該功能呢?答案很簡單只需要加上-C(注意是大寫C)或者--compress=true選項即可,事情看似簡單,但是背后的設計卻值得大家深入挖掘,啟動后到底發生什么事情了,對網絡傳輸性能上的提升到底有什么影響呢?
趁着風和日麗的今天,正適合在西湖邊上一邊泡着龍井、一邊聽着音樂、一邊曬着太陽來享受一下這里面的真相,好,廢話不多說,咱們開始吧。
首先,當客戶端加上-C時,就會在Capabilities上添加CLIENT_COMPRESS壓縮標志,源碼參考sql-common/client.c的default_options變量、option_id變量以及mysql_read_default_options函數:
(PS:后面有出現源碼的部分都是引用mysql 5.5.36版本,另外,核心接口部分的源碼從5.1到5.6多個版本很少會變)
但這里有個問題,這樣是否就成功開啟壓縮功能呢?不行的,還記得開始的時候提到的必須要客戶端和服務端都開啟壓縮功能才是成功的,那服務端又是什么時候告訴客戶端支持壓縮呢?那就是在tcp三次握手后,服務端就會給客戶端發送一個handshake initialization包,源碼參考include/mysql_com.h的CAN_CLIENT_COMPRESS和sql/sql_acl.cc的send_server_handshake_packet函數:
如果在編譯源碼時沒定義HAVE_COMPRESS變量時,那么服務端就不支持壓縮,一般情況不會去掉該選項。
接下來,大伙得加快速度,跟上步伐,一起來認識下壓縮協議包組成部分,這主要由Compressed Packet header和payload組成,具體如下圖所述:
圖1 壓縮協議組成
從圖中可以看出比普通的協議多出3個字節,細心的讀者會有個疑問,為什么Sequence Id是帶有compressed,其中的作用是?不着急哈,下面會慢慢揭曉為什么會有一個獨立的compressed sequence id。
另外,可能細心的讀者又有疑問,為什么消息體是Compressed Payload或者Uncompressed Payload?這是因為mysql內部有一個約定,如果查詢語句payload小於50字節時,對內容不壓縮而保持原貌的方式,而mysql此舉是為了減少CPU性能開銷,源碼參考include/my_sys.h的MIN_COMPRESS_LENGTH和mysys/my_compress.c的my_compress函數:
同時,壓縮前的長度會設置為0。
如果消息體為Compressed Payload時,客戶端或服務端交互前,可能會將一個或多個MySQL包文合並壓縮成一個數據包再發出去,目的顯然而見,為了提升網絡傳輸性能,對於一些網絡環境較差的用戶會有很大的幫助,剛才有提到過compressed sequence id的問題,如果不使用一個單獨的變量來標志的話,那么當一個壓縮包里有多個MySQL報文時就不知道怎么確定包序號了,這就是該變量的作用了。
那這里面貌似還會產生一個問題,如果原始報文或拼揍后是32M的話,能否進行壓縮呢?答案是不行的,因為mysql一個包文最大長度限制為16M,從上面圖1的第3部分length of payload before compression就可以判斷出來,源碼參考mysys/my_compress.c的my_uncompress函數:
以上就是今天要分享的小細節,希望對大家理解壓縮協議方面的相關細節有所幫助,祝玩得開心!