如何用Go語言打造一個高性能MySQL Proxy

kingshard架構設計和功能實現

kingshard(https://github.com/flike/king...開源有一段時間了,有些熱心的用戶發郵件來諮詢kingshard的設計和實現問題。因而週末抽空寫了一篇介紹kingshard架構和功能實現的文章,但願經過本文可以讓用戶對kingshard有更深的瞭解。下面分模塊來介紹kingshard的核心組件的設計和實現。node

1. 總體架構

kingshard採用Go開發,充分地利用了Go語言的併發特性。Go語言在併發方面,作了很好的封裝,這大大簡化了kingshard的開發工做。kingshard的總體工做流程入下所述:git

  1. 讀取配置文件並啓動,在配置文件中設置的監聽端口監聽客戶端請求。
  2. 收到客戶端鏈接請求後,啓動一個goroutine單獨處理該請求。
  3. 首選進行登陸驗證,驗證過程徹底兼容MySQL認證協議,因爲用戶名和密碼在配置文件中已經設置好,因此能夠利用該信息驗證鏈接請求是否合法。
    當用戶名和密碼都正確時,轉入下面的步驟,不然返回出錯信息給客戶端。
  4. 認證經過後,客戶端發送SQL語句。
  5. kingshard對客戶端發送過來的SQL語句,進行詞法和語義分析,識別出SQL的類型和生成SQL的路由計劃。若是有必要還會改寫SQL,而後轉發到相應的DB。也有可能不作詞法和語義分析直接轉發到相應的後端DB。若是轉發SQL是分表且跨多個DB,則每一個DB對應啓動一個goroutine發送SQL和接收該DB返回的結果。
  6. 接收併合並結果,而後轉發給客戶端。

kingshard工做總體流程可參考下面這幅圖。github

kingshard流程圖

kingshard總體架構圖以下所示web

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),語義分析就是基於這棵語法樹來操做的。語義分析的目的主要有如下幾個方面:算法

  1. 讀寫分離,只有識別出SQL語句的類型,才能進行正確的讀寫分離操做。
  2. 數據分片,解析出表名和查詢條件,將SQL路由到正確的DB。
  3. SQL黑名單,經過詞法和語義分析,也能夠快速識別出須要屏蔽的SQL語句。例如,檢測到delete語句不帶where操做,就能夠直接阻斷該SQL的轉發。

kingshard並無考慮徹底兼容MySQL全部語法,由於徹底兼容MySQL語法會使得詞法和語義分析模塊變得異常複雜,並且低效。對於DDL語句其實不必解析,只要能正確轉發到後端相應的DB上就能夠。sql

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@3
kingshard在讀取配置信息初始化系統的時候,就生成了一個輪詢數組:[0,0,1,1,1]。在kingshard中會將這個數組打亂順序,變成:[0,1,1,0,1]。這樣就避免了動態計算DB下標的問題,對性能提高有必定幫助。數組

4.sharding實現

首選須要介紹兩個概念:安全

  1. 子表,在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。

  1. Node,子表分佈在各個node上,每一個node包含一個maser server和若干個slave server(slave個數能夠爲0)。寫請求會發往該node中master server,讀請求會發往該node中的slave server。

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上的事務流程以下:

  1. 用戶發送begin語句。
  2. kingshard接收到SQL語句後,將該鏈接的狀態設置爲事務。
  3. 用戶發送DML語句,kingshard識別出語句須要發送到的DB,而後kingshard新建一個與後端DB的鏈接中取一個鏈接,利用該鏈接發送語句。
  4. 收取SQL語句的結果後,將鏈接放回。
  5. kingshard收到下一條SQL語句,判斷該SQL是否是發往同一個DB,若是不是則報錯。若是是發往同一個DB,則利用該鏈接發送語句。
  6. 收到用戶發送的commit語句,將該鏈接的狀態設置爲非事務,事務結束。

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)給我。

9. 公衆號

歡迎關注後端技術快訊公衆號,有關kingshard的最新消息與後端架構設計類的文章,都會在這個公衆號分享。圖片描述

相關文章
相關標籤/搜索