如何用Go語言打造一個高性能MySQL Proxy
kingshard架構設計和功能實現
kingshard(https://github.com/flike/kingshard) 開源有一段時間了,有些熱心的用戶發郵件來咨詢kingshard的設計和實現問題。于是周末抽空寫了一篇介紹kingshard)架構和功能實現的文章,希望通過本文能夠讓用戶對[v有更深的了解。下面分模塊來介紹kingshard的核心組件的設計和實現。
1. 整體架構
kingshard采用Go開發,充分地利用了Go語言的并發特性。Go語言在并發方面,做了很好的封裝,這大大簡化了kingshard的開發工作。kingshard的整體工作流程入下所述:
- 讀取配置文件并啟動,在配置文件中設置的監聽端口監聽客戶端請求。
- 收到客戶端連接請求后,啟動一個goroutine單獨處理該請求。
- 首選進行登錄驗證,驗證過程完全兼容MySQL認證協議,由于用戶名和密碼在配置文件中已經設置好,所以可以利用該信息驗證連接請求是否合法。 當用戶名和密碼都正確時,轉入下面的步驟,否則返回出錯信息給客戶端。
- 認證通過后,客戶端發送SQL語句。
- kingshard對客戶端發送過來的SQL語句,進行詞法和語義分析,識別出SQL的類型和生成SQL的路由計劃。如果有必要還會改寫SQL, 然后轉發到相應的DB。也有可能不做詞法和語義分析直接轉發到相應的后端DB。如果轉發SQL是分表且跨多個DB,則每個DB對應啟動一個 goroutine發送SQL和接收該DB返回的結果。
- 接收并合并結果,然后轉發給客戶端。 </ol>
- 讀寫分離,只有識別出SQL語句的類型,才能進行正確的讀寫分離操作。
- 數據分片,解析出表名和查詢條件,將SQL路由到正確的DB。
- SQL黑名單,通過詞法和語義分析,也可以快速識別出需要屏蔽的SQL語句。例如,檢測到delete語句不帶where操作,就可以直接阻斷該SQL的轉發。 </ol>
-
子表,在kingshard中一張邏輯上的大表由若干張小的子表組成。例如:將stu表分成stu_0000,stu_0001,stu_0002,stu_0003。 在數據庫中stu表是不存在的,它只是一張邏輯上的表。數據庫中只存在四張子表(stu_0000,stu_0001,stu_0002,stu_0003)。 發送SQL語句時,kingshard會識別出需要分表的SQL語句,并改寫該SQL。例如,客戶端發送過來的SQL語句是:select name from stu where id =10;kingshard收到該SQL語句后,從配置信息中識別出該表是一個Hash類型的分表。根據分表規則,將該SQL改寫成:select name from stu_2 where id =10;然后發送給對應的DB。
</li> -
Node,子表分布在各個node上,每個node包含一個maser server和若干個slave server(slave個數可以為0)。寫請求會發往該node中master server,讀請求會發往該node中的slave server。
</li> </ol>kingshard的sharding采用了兩級映射的思想,首選根據SQL語句的分表條件計算出這條SQL語句落在哪個子表上,然后再根據配置信 息找到該子表 落在哪個node上。采用兩級映射的思想,對于MySQL的擴容和縮容都能很方便地支持。目前kingshard sharding支持insert, delete, select, update和replace語句, 所有這五類操作都支持跨子表。但寫操作僅支持單node上的跨子表,select操作則可以跨node,跨子表。
對于有些表沒有分表,操作該表的SQL語句會發往default node。或者用戶可以選擇在SQL語句前面加上注釋,指定該SQL要發往的node,kingshard接收到語句后,識別出注釋中指定的node,然 后將該SQL發往對應node中合適的DB。例如用戶發送/*node1*/select * from member where id=100,kingshard接收到該SQL后會將其發送到node1的salve上。這樣kingshard就能很好地兼容分表和不分表的各種應用場景了。
5. 事務的實現
所有proxy支持shard后都會面臨一個問題:支不支持分布式事務?出于性能和可用性考慮, kingshard只支持單臺DB上的事務,不允許跨DB的事務。kingshard處理單DB上的事務流程如下:
- 用戶發送begin語句。
- kingshard接收到SQL語句后,將該連接的狀態設置為事務。
- 用戶發送DML語句,kingshard識別出語句需要發送到的DB,然后kingshard新建一個與后端DB的連接中取一個連接,利用該連接發送語句。
- 收取SQL語句的結果后,將連接放回。
- kingshard收到下一條SQL語句,判斷該SQL是不是發往同一個DB,如果不是則報錯。如果是發往同一個DB,則利用該連接發送語句。
- 收到用戶發送的commit語句,將該連接的狀態設置為非事務,事務結束。 </ol>
6. 后端DB存活檢測
kingshard每個node啟動了一個goroutine用于檢測后端master和slave的狀態。當goroutine持續一段時間(由 配置文件中down_after_noalive參數設置)ping不通后端的DB后,會將該DB的狀態設置為down,后續kingshard就不會將 sql語句發往該DB了。
7. 客戶端白名單機制
有時候用戶為了安全考慮,希望只能某幾臺server能夠連接kingshard。在kingshard的配置文件中有一個參 數:allow_ips,用于實現客戶端白名單機制。當管理員設置了該參數,則意味著只有allow_ips指定的IP能夠連接kingshard,其他 IP都會被kingshard拒絕連接。如果不設置該參數,則連接kingshard的客戶端不受限制。
8. 管理端設計和實現
kingshard的管理端口復用了工作端口,通過特定的關鍵字(admin)來標示。kingshard是通過對管理端特定的SQL進行詞法和語 義分析,將SQL語句解析為一條kingshard可以識別的命令。目前支持平滑上下線master和slave,和查看kingshard配置和后端 DB狀態。后續打算將web頁面集成到管理端,這樣用戶就可以不用輸入命令行操作,而是在網頁上操作。大大降低用戶使用kingshard的門檻。
上述各個模塊都是kingshard中比較核心的模塊,通過這篇文章的介紹,我想讀者應該對kingshard的架構和實現有了初步的了解。很多功 能的設計和實現,都是作者慢慢地摸索和實踐。如果有讀者對kingshard的設計或實現感興趣或者對上述設計有不同的想法,歡迎發郵件 (flikecn#126.com)給我。
開源地址:
github:https://github.com/flike/kingshard
OSC:http://git.oschina.net/flikecn/kingshard
來自:http://my.oschina.net/flikecn/blog/493172
kingshard工作整體流程可參考下面這幅圖。 kingshard整體架構圖如下所示
2. 詞法和語義分析
要將kingshard的功能做的足夠強大,就不得不進行SQL的詞法和語義分析。SQL語句的詞法分析指的是將SQL語句切分成一個一個的關鍵字。例如對SQL語句:select name from stu where id < 13進行詞法分析,得到的結果是:{"select","name","from","stu","where","id","<","13"}。 這樣做的目的主要為了生成一棵抽象語法樹,也就是大家常說的AST(abstract syntax tree),語義分析就是基于這棵語法樹來操作的。語義分析的目的主要有以下幾個方面:
kingshard并沒有考慮完全兼容MySQL所有語法,因為完全兼容MySQL語法會使得詞法和語義分析模塊變得異常復雜,而且低效。對于DDL語句其實沒必要解析,只要能正確轉發到后端相應的DB上就可以。
kingshard只對部分DML語句(select,update,insert,delete,replace)進行了解析,這樣可以滿足絕大部分的分表操作。對于其他語句,kingshard會將其發送到一個默認的DB,或者通過kingshard特有的方式將其發送到指定的DB上,例如:/*node2*/insert into stu(id,name) values(12,'xiaoming'),對于這種帶有注釋的的sql語句,kingshard能夠識別出,然后將這條sql語句發送到node2節點的Master DB上。
3. 負載均衡
用戶使用Mysql Proxy目的很大一部分就是為了降低單臺DB的負載,將讀壓力分擔到多臺DB上。kingshard支持多個slave,不同的slave可以配置不同 的讀權重,權重越大分擔的讀請求越多。kingshard讀請求負載均衡采用的是權重輪詢調度算法。
大部分系統采用該算法時,都是轉發SQL語句時,動態地計算出本次選取DB的序號。然后將讀請求的SQL語句發送到該DB。仔細分析一下,這樣做其 實是沒有必要的。因為DB的權重是相對固定的,不會經常變動,所以完全可以計算出一個固定的輪詢序列,然后將這個序列保存在一個數組中。這樣不需要動態計 算,每次讀取數組就可以。舉個例子來說,在kingshard的node配置項中配置slave選項:slave:192.168.0.12@2,192.168.0.13@3kingshard在讀取配置信息初始化系統的時候,就生成了一個輪詢數組:[0,0,1,1,1]。在kingshard中會將這個數組打亂順序,變成:[0,1,1,0,1]。這樣就避免了動態計算DB下標的問題,對性能提升有一定幫助。
4.sharding實現
首選需要介紹兩個概念: