MySQL客戶端代碼引發(fā)的思考
一次偶然的機會debug代碼瞅了一眼Mysql客戶端的代碼(Java版本),最引發(fā)我興趣的是這一句:
execSQL是一個非常重要的方法,所有SQL語句的最終都是交由這個代碼來實現(xiàn)的;這個方法被一個connectionMutex鎖(這個鎖其實就是Connection對象自己,Mysql客戶端通過使用了“反射”所以不能直接使用this)包裹著這就意味著——在同一時刻一個Mysql連接只能執(zhí)行一條SQL語句。
MySQL客戶端的“鎖”
帶著疑問我走讀了這段代碼,畫一幅圖
MysqlIO是負責(zé)網(wǎng)絡(luò)通訊的底層類,使用的是Java是BIO通訊屬于“標準”的TCP客戶端寫法。它內(nèi)部有三個重要的成員變量
- mysqlConnection是一個Socket類型
- mysqlOutput是一個BufferedOutputStream類型
- InputStream是一個InputStream類型(Mysql做了一點封裝理解成BufferedInputStream也沒什么問題)
這三個成員變量的初始化是在MySqlIO的構(gòu)造函數(shù)完成的,當執(zhí)行SQL語句的時相當于:
- 鎖住Socket,此時只有當前連接可以使用這個Socket對象(實際的鎖范圍更大)
- mysqlOutput寫入數(shù)據(jù)包,發(fā)送Command(內(nèi)部稱客戶端->服務(wù)器的請求為Command)
- mysqlInput讀取數(shù)據(jù)包,解析返回結(jié)果
如果有多條SQL語句遞交給一個連接因為Socket被“鎖”所以會變成串行執(zhí)行。
這不是bug,是特性
帶著疑問我翻看了Mysql的通訊協(xié)議,客戶端->服務(wù)器的標準格式如下:
- 3字節(jié)表示后面“payload”的長度(所以mysql單數(shù)據(jù)包***值是4M);
- 1字節(jié)表示“sequence_id”;這個字段似乎沒有什么用途
- ***一個是變長的“payload”(長度由***部分決定)
服務(wù)器端->客戶端(響應(yīng)數(shù)據(jù)包)的標準格式是
- 1字節(jié)表示“類型”
- 根據(jù)不同的類型“payload”會有不同的變化,比如返回的錯誤信息、受影響的行數(shù)等
看到這里我明白原因了。
Mysql的數(shù)據(jù)包中沒有辦法區(qū)分出一個連接中“不同的數(shù)據(jù)包”。A、B兩條SQL語句,可以通過一個Socket發(fā)送到服務(wù)器端;服務(wù)器端也會返回兩個執(zhí)行結(jié)果。問題是——客戶端如何區(qū)分出哪個是A的執(zhí)行結(jié)果哪個是B的執(zhí)行結(jié)果呢?
仔細觀察上面的數(shù)據(jù)包唯一的方式是sequence_id,A分配一個id,B分配一個;服務(wù)器端在返回的時候把相應(yīng)的sequence_id帶回來表示這是某個SQL語句的執(zhí)行結(jié)果。遺憾的是——sequence_id根本沒用到,服務(wù)器端不會返回sequence_id信息。(看Mysql的響應(yīng)數(shù)據(jù)包)
正是因為這個原因?qū)е铝怂械腗ysql客戶端Connection對象都不是線程安全的。如果想要同時執(zhí)行多條SQL語句就只能構(gòu)造多個Connection。
有意思的是MongoDB的協(xié)議格式幾乎和Mysql的一樣,messageLength,requestID,opCode;但是響應(yīng)數(shù)據(jù)包“修正”了這個bug,messageLength,responseID,opCode。這就意味著mongodb的conneciton是不需要阻塞的而且根本不需要“線程池”。(根據(jù)我觀察connectionsPerHost似乎是沒有用到,難道這個是為了給大家“安全感”?——我們帶線程池,放心用吧)
更多思考
我想到了更多東西,HTTP 2.0的多路復(fù)用(Multiplexing)。在HTTP1.0中每個HTTP請求都是一個TCP請求,瀏覽器載入頁面的時候會大量加載css、js、圖片、html短時間內(nèi)會發(fā)起大量的TCP請求。在HTTP2.0中同時向一臺主機發(fā)起css、js、圖片可以被承載在同一個TCP連接中。
AMQP中也有相似的設(shè)計,叫Channel;一個TCP連接可以被多個線程同時使用。比如用一個TCP連接可以同時實現(xiàn)“訂閱”和“發(fā)布”消息。
微軟RDP協(xié)議中也有相同的設(shè)計,區(qū)分圖片、聲音、鼠標、鍵盤操作(Citrix的ICA設(shè)計號稱的32個通道就是這個意思)。
SSH協(xié)議中有一個叫Channel Mechanism的東西,它也是為了實現(xiàn)“多路復(fù)用”的(這意味著提高了ansible的效率)。
技術(shù)就是這么奇妙,很多東西都是你“借鑒我”,我“借鑒你”。相同的問題相同的解決辦法,如果用心其實可以匯編成一本書,比如我們上面講的或許就可以叫——“多路復(fù)用的協(xié)議設(shè)計模式”吧。
【本文是51CTO專欄作者邢森的原創(chuàng)文章,轉(zhuǎn)載請聯(lián)系作者本人獲取授權(quán)】