1. 程式人生 > >如何用Go語言打造一個高效能MySQL Proxy

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

kingshard架構設計和功能實現

kingshard(https://github.com/flike/kingshard)開源有一段時間了,有些熱心的使用者發郵件來諮詢kingshard的設計和實現問題。於是週末抽空寫了一篇介紹kingshard架構和功能實現的文章,希望通過本文能夠讓使用者對kingshard有更深的瞭解。下面分模組來介紹kingshard的核心元件的設計和實現。

1. 整體架構

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

  1. 讀取配置檔案並啟動,在配置檔案中設定的監聽埠監聽客戶端請求。
  2. 收到客戶端連線請求後,啟動一個goroutine單獨處理該請求。
  3. 首選進行登入驗證,驗證過程完全相容MySQL認證協議,由於使用者名稱和密碼在配置檔案中已經設定好,所以可以利用該資訊驗證連線請求是否合法。
    當用戶名和密碼都正確時,轉入下面的步驟,否則返回出錯資訊給客戶端。
  4. 認證通過後,客戶端傳送SQL語句。
  5. kingshard對客戶端傳送過來的SQL語句,進行詞法和語義分析,識別出SQL的型別和生成SQL的路由計劃。如果有必要還會改寫SQL,然後轉發到相應的DB。也有可能不做詞法和語義分析直接轉發到相應的後端DB。如果轉發SQL是分表且跨多個DB,則每個DB對應啟動一個goroutine傳送SQL和接收該DB返回的結果。
  6. 接收併合並結果,然後轉發給客戶端。

kingshard工作整體流程可參考下面這幅圖。
kingshard流程圖
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),語義分析就是基於這棵語法樹來操作的。語義分析的目的主要有以下幾個方面:

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

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:[email protected],[email protected]
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。

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

相關推薦

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

kingshard架構設計和功能實現 kingshard(https://github.com/flike/kingshard)開源有一段時間了,有些熱心的使用者發郵件來諮詢kingshard的設計和實現問題。於是週末抽空寫了一篇介紹kingshard架構和功

Go語言打造區塊鏈[1]

學好Go語言走遍天下都不怕。以下程式碼建立了一個非常原始的電子賬本: package main import ( "bytes" "crypto/sha256" "f

go語言一個簡單的登入,大家不妨來看一下

現在的網站可以說登入註冊幾乎就是首要的,所以今天就給大家來一個簡約版的登入吧 先來給大家看一下效果吧 有些過分簡潔,大家別見怪啊 接下來是成功的效果 我只是提供一個思路和方法至於美化,大家隨意 接下來是失敗的效果 接下來就是程式碼了 前

Go 語言實現一個 telegram 的 bot

嘗試 用 Go 語言做了一個 telegram 的 bot . 用來簡單實現對話(復讀)。   獲得 tele

Go語言打造分散式Crontab 輕鬆搞定高效能任務排程

第5章 應用mongodb實現分散式儲存 mongodb是一個分散式的海量儲存服務,常用於儲存大量的日誌類資料。本章中,將首先分析mongodb優勢、原理、應用場景,讓大家對其架構和功能有所瞭解。然後,我們會搭建mongodb服務端,開發若干示例程式,包括:插入,查詢,刪除。後續實戰課將使用mongo

2018年Go語言打造分散式Crontab 輕鬆搞定高效能任務排程

第1章 課程介紹 本章中將介紹一下本課程的基本內容,包括:我們要做什麼、要求什麼基礎、將學會哪些工具、收穫哪些獨家乾貨,以及課程具體安排。 第2章 如何執行shell命令 執行"定時任務"其實就是執行"shell命令"。在本章中,將首先帶大家區分"程式"與"命令",接著瞭解

windows下Go語言實現第一個hello world

1,下載go編譯器———go編譯器下載地址https://golang.org/dl/ go編譯器下載地址 2,然後點選進行安裝,由於是msi檔案,如果需要.NET元件請自行下載進行安裝

html語言一個功課表

ble mage ima http jsb type 聲明 mil .net 今天在網上看了一個關於html的教程,主要是講表格,看完之後認為有必要上機試試。於是就寫了以下的一段代碼。 <!DOCTYPE html><!--貌似5.0的能

Go語言建立一個最簡單的服務端點

一個 nds Coding port struct pac quest com handler handlers/handlers.go package handlers import ( "encoding/json" "net/http" )

C語言一個好玩的寶石一樣的圖像

循環圖像圖形#include<stdio.h>main(){int n;scanf("%d",&n); for(int i=0;i<n;i++) { for(int j=0;j<n-i-1;j++) { printf(" ")

怎麽C語言一個飛機程序

sge 怎麽 das mob gpl hdd ref dhx cte 5zeffurgal誥退俁稼犢爍厝擋傅鼓《http://weibo.com/p/230927987816857564094464》 wv8kbiqhdd壁溉斡吠戎琴痛鋼匙谷《http://weibo.c

python語言一個簡單的計算器

ali pla ket 列表 調用 語言 括號 lac 跳出循環 假如我們有這樣一個式子: 1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )思

go語言實現類似java8的Stream

4.4 代碼 歸納 Go語言 浮點 print jdk 是我 遞歸 JDK8 Stream 是一個支持泛型和函數式數據流,使用起來非常強大方便。最近在學習 go 語言我就用 go 模仿了一下類似的功能,由於 go 對泛型、函數式的支持比較有限,感覺泛型和函數式這一塊實現起來

go語言寫的快速排序

因為工作原因接觸了go語言,由於其特性,並行程式設計非常方便。而go語言特有的入門級的特性最主要的就包括了用go開協程,用channel進行同步。 用這些入門級特性寫了一個多執行緒版本的快速排序是一個非常好的練習。 package main import ( "fmt" "sync"

c語言實現一個簡單的通訊錄

通訊錄的c語言實現原始碼 簡單通訊錄的實現還是包括三個原始檔,test.c(實現通訊錄主邏輯),txl.c(實現用到的各個函式),txl.h(存放txl中用到的各種標頭檔案與宣告)。 txl.h #ifndef __TXL_H__//**txl.h** #defi

Go語言10-http和mysql

http 程式設計 Go 原生支援http: import "net/http" Go 的http服務效能和nginx比較接近:就是說用Go寫的Web程式上線,程式前面不需要再部署nginx的Web伺服器,這裡省掉的是Web伺服器。如果伺服器上部署了多個Web應用,還是需要反向代理的,一般這也是ngin

C語言一個簡單的三子棋,實現玩家與電腦的對戰

原始碼: #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <windows.h> #include <time.h> /* 用 C 寫一個三子棋 */ //邏輯: //1. 畫

C語言一個簡單的掃雷小遊戲

#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <windows.h> #include <time.h> /* 用 C 語言寫一個簡單的掃雷遊戲 */ // 1.寫一個遊戲選單 M

在windows下go語言寫跨卷批量移動小工具

時值我小病在家休養生息,喜歡跳廣場舞的外公來尋求我的幫助,他們跳廣場舞是將存有歌曲的U盤插到音響上面,而音響大部分都是隻能顯示歌曲的索引index,不能直接顯示歌曲名,所以為了方便他們會在U盤裡面對歌曲進行排序。由於音響是定址按順序播放,意思就是在U盤裡面的歌曲需要一首一首的按順序複製過去,而且當對U盤歌曲進

如何打造一個高效能、高併發的訊息推送系統

前言 女友常常勉勵我:“要有共享、開放、開源的現代網際網路思維,自己的經驗要多總結,發到部落格論壇上什麼的。”之前也有腦洞開啟,想分享一些個人在工作之中、工作之外的所思所得,可始終不能持久。這次想把本次參與開發的專案記錄、分享出來,希望能持之以恆。 part 1 即時通訊與訊息推送