一、mysql-proxy簡介
mysql-proxy是mysql官方提供的mysql中間件
服務(wù),上游可接入若干個mysql-client,后端可連接若干個mysql-server。
它
使用mysql協(xié)議,任何使用mysql-client的上游無需修改任何代碼,即可遷移至mysql-proxy上。
mysql-proxy最基本的用法,就是作為一個請求攔截,請求中轉(zhuǎn)的中間層:

進(jìn)一步的,mysql-proxy可以分析與修改請求。
攔截查詢和修改結(jié)果,需要通過編寫Lua腳本來完成。
mysql-proxy允許用戶指定Lua腳本對請求進(jìn)行攔截,對請求進(jìn)行分析與修改,它還允許用戶指定Lua腳本對服務(wù)器的返回結(jié)果進(jìn)行修改,加入一些結(jié)果集或者去除一些結(jié)果集均可。
所以說,根本上,mysql-proxy是一個官方提供的框架,具備良好的擴展性,可以用來完成:
?sql攔截與修改
?性能分析與監(jiān)控
?讀寫分離
?請求路由
?...
這個框架提供了6個hook點,能夠讓用戶能夠動態(tài)的介入到client與server中的通訊中去。
二、mysql-proxy架構(gòu)與原理
如“簡介”中所述,mysql-proxy向用戶提供了6個hook點,讓用戶實現(xiàn)Lua腳本來完成各種功能,這些hook點是以函數(shù)的形式提供的,用戶可以實現(xiàn)這些函數(shù),在不同事件、不同操作發(fā)生時,做我們期望的事情。
connect_server()mysql-client向proxy發(fā)起連接時,proxy會調(diào)用這個函數(shù)。用戶可以實現(xiàn)該函數(shù),來做一些負(fù)載均衡的事情,例如選擇將要連向那個mysql-server。假設(shè)有多個mysql-server后端,而用戶又沒有實現(xiàn)這個函數(shù),proxy默認(rèn)采用輪詢(round-robin)策略。
read_handshake()mysql-server向proxy返回“初始握手信息”時,proxy會調(diào)用這個函數(shù)。用戶可以實現(xiàn)這個函數(shù),來做更多的權(quán)限驗證工作。
read_auth()mysql-client向proxy發(fā)送認(rèn)證報文(user_name, password,database)時,proxy會調(diào)用這個函數(shù)。
read_auth_result()mysql-server向proxy返回認(rèn)證結(jié)果時,proxy會調(diào)用這個函數(shù)。
read_query()認(rèn)證完成后,mysql-client每次經(jīng)過proxy向mysql-server發(fā)送query報文時,proxy會調(diào)用這個函數(shù)。用戶如果要攔截請求,就可以模擬mysql-server直接返回了,當(dāng)然用戶亦可以實現(xiàn)各種策略,修改請求,路由請求等各種不同的業(yè)務(wù)邏輯。
read_query_result()認(rèn)證完成后,mysql-server每次經(jīng)過proxy向mysql-client返回query結(jié)果時,proxy會調(diào)用這個函數(shù)。需要注意,如果用戶沒有顯示實現(xiàn)read_query()函數(shù),則read_query_result()函數(shù)是不會被調(diào)用的。用戶可以在此處實現(xiàn)各種合并策略,或者對結(jié)果集進(jìn)行修改。
下圖是一個各hook函數(shù)的觸發(fā)架構(gòu)圖,箭頭方向表示觸發(fā)時機:

可以發(fā)現(xiàn),
最重要的兩個函數(shù)其實是read_query()和read_query_result(),各種sql的改寫與結(jié)果集的改寫邏輯,都是在這兩個函數(shù)中實現(xiàn)的,更細(xì)節(jié)的query過程如下圖:

三、mysql-proxy典型應(yīng)用
案例一: sql時間統(tǒng)計分析
假設(shè)mysql-client提交的原sql為:
XYZ;
proxy可以在read_query()里將其改寫為:
SELECT NOW();
XYZ;
SELECT NOW();
這樣在返回結(jié)果集時,就可以在應(yīng)用層對sql時間進(jìn)行記錄,以方便統(tǒng)計分析。
案例二:sql性能統(tǒng)計分析
假設(shè)mysql-client提交的原sql為:
XYZ;
proxy可以在read_query()里將其改寫為:
XYZ;
EXPLAIN XYZ;
這樣在返回結(jié)果集時,就可以在應(yīng)用層對sql性能進(jìn)行記錄,以方便統(tǒng)計分析。
需要強調(diào)的是,這兩個案例,由于proxy在read_query()時對sql進(jìn)行了改寫,故在read_query_result()時,mysql-server其實返回了比原請求更多的信息,proxy一定要將多余的信息去掉,再返回mysql-client。多說一句,可以
加入一個唯一ID,來對請求sql和返回結(jié)果進(jìn)行配對。
案例三:讀寫分離
mysql-proxy啟動時,通過參數(shù)即可配置后端mysql-server是主server還是read-only,無需修改任何代碼:
shell> mysql-proxy \
--proxy-backend-addresses=10.0.1.2:3306 \
--proxy-read-only-backend-addresses=10.0.1.3:3306
注意,這里的兩個mysql-server為主從架構(gòu)。
案例四:性能水平擴展
mysql-proxy啟動時,通過參數(shù)配置多個后端,即可實現(xiàn)性能的水平擴展,無需修改任何代碼:
shell> mysql-proxy \
--proxy-backend-addresses=10.0.1.2:3306 \
--proxy-backend-addresses=10.0.1.3:3306
注意,這里的兩個mysql-server為主主架構(gòu),如果不做特殊修改,負(fù)載均衡策略為round-robin。
四、mysql-proxy其他問題
提問:Lua腳本引入的額外開銷有多大?
官網(wǎng)回答:Lua很快,對于大部分應(yīng)用來說,額外開銷很小,原始包(raw packet)開銷大概在400微秒左右。
樓主:這,,,我不太相信。
提問:mysql-proxy和mysql-server可以部署在一臺機器上么?
官網(wǎng)回答:proxy單獨部署也可以,和mysql部署在同一臺機器上也可以。相比mysql而言,proxy不怎么占CPU和內(nèi)存,其性能損耗可以忽略不計。
樓主:這,,,性能損耗可以忽略,這我也不太信。
提問:proxy可以處理SSL連接么?proxy不會獲取和保存我的明文密碼吧?
官網(wǎng)回答:作為中間人,不能處理加密信息。不會獲取密碼,也獲取不到。mysql協(xié)議不允許密碼以明文傳輸,傳輸?shù)亩际羌用芎蟮拿芪摹?br />
提問:在Lua腳本里可以使用LuaSocket,連緩存,連其他服務(wù)么?
官網(wǎng)回答:理論上可以。但是,大哥,你確定要這樣做么,強烈不建議這樣。
更多建議: