天天看點

MySQL資料庫讀寫分離中間件Atlas

作者:朱超,奇虎360公司資深研發工程師,2011年加入公司,先後負責資料庫中間件、分布式消息隊列、配置管理系統等軟體基礎設施的設計與開發,對高性能分布式系統架構有濃厚興趣。

本文為《程式員》原創文章,未經允許不得轉載,更多精彩文章請訂閱2017年《程式員》

Atlas是由奇虎360公發的基于MySQL協定的資料庫中間件産品,它在MySQL官方推出的MySQL-Proxy 0.8.2版本的基礎上,修改了若幹Bug,并增加了很多功能特性。目前該産品在360内部得到了廣泛應用,覆寫80%以上的MySQL業務,每天讀寫量達數十億次,于GitHub開源後,業界幾十家公司将其應用于生産環境。

Atlas項目源代碼托管位址https://github.com/qihoo360/atlas。

産品研發背景

該項目最早在2012年提出,主要為了解決兩個問題:

一是業務程式員對資料庫細節關注過多。在沒有中間件的情況下,應用程式直接連接配接MySQL的主從庫,需要在配置檔案中指定主從庫的IP和端口,并由業務程式員自行決定将寫語句和讀語句分别發往主庫和從庫,在資料量較大的情況下還需要自行管理分庫分表等技術細節,使得業務程式員負擔較重。

二是DBA運維工作不友善。資料庫當機是家常便飯,在需要切換或上下線資料庫時,DBA需要協調業務程式員修改配置,運維操作也會對業務造成一定幹擾,影響DBA工作順利進行。

我們希望能将業務與DB進行一定程度的隔離,使業務程式員盡量少關心DB的細節,可以專注于編寫業務邏輯;另一方面DBA的運維操作盡量做到對業務無影響。如此則需要将讀寫分離、分庫分表、平滑上下線DB等邏輯提煉出來,以公共元件的方式提供給以上兩類人群使用。從形式上看有兩種,一種是獨立的中間件服務,另一種內建于用戶端的LIB。前者優點是更新更新較友善,而且與語言無關,缺點是網絡增加一跳,資料需要轉發,性能可能不如LIB形式,後者優缺點正相反。經過權衡我們選擇了前者,除以上考慮外還有部分原因是我們團隊與各條業務線是獨立的部門,要推動業務工程師更新LIB有相當的困難。

開始時調研了MySQL官方的MySQL-Proxy、阿裡巴巴的Cobar、Amoeba、TDDL等産品,其中MySQL-Proxy更新緩慢,長期停留在Alpha版階段,Cobar安裝部署相對複雜,Amoeba不支援事務,TDDL未完整開源。最終我們選擇了MySQL-Proxy進行二次開發。

技術架構

部署架構

Atlas一般搭配LVS使用,用戶端通過LVS通路Atlas,以避免單點故障,當然也可以使用Keepalived等。Atlas後面挂接MySQL的一主多從叢集,需要注意的是主從同步需要DBA預先配置好,Atlas隻涉及用戶端與DB間的網絡互動,DB與DB間的通訊與Atlas無關。

MySQL資料庫讀寫分離中間件Atlas

圖1 讀寫分離架構

Atlas會把用戶端發來的SQL語句根據讀寫分别路由到主庫和從庫上,對于有多台從庫的情況,還會根據權重負載均衡政策決定選擇哪台從庫來執行每條讀語句。

線程模型

Atlas的線程模型與Memcached相似,都是由一個主線程和若幹個工作線程組成。主線程負責監聽用戶端的新連接配接,工作線程在系統啟動時即建立完成,并監聽管道讀端的讀事件。當有用戶端連接配接到來時,主線程将用戶端的IP和端口等資訊封裝成一個CON結構,然後選擇某個工作線程來處理該用戶端的請求。選擇的方法可以是Round-Robin方式,也可以根據各個線程的負載情況選擇負載最輕的線程(可以粗略認為待處理隊列最短即為負載最輕)。主線程標明工作線程後,将CON結構放入該線程對應的待處理隊列,然後向相應管道的寫端寫入一個位元組(位元組内容無所謂),則工作線程會收到管道讀端的讀事件,并從隊列中取出CON結構,進行下一步處理。當然也可改用Linux的新系統調用eventfd實作對工作線程的喚醒,性能比管道更高。

MySQL資料庫讀寫分離中間件Atlas

圖2 線程模型

兩個經典技術問題

字元集

MySQL支援多種字元集,字元集狀态與會話(連接配接)綁定,即各個連接配接上的字元集互不相幹。而Atlas擁有連接配接池,要求實作連接配接複用,這就不可避免會導緻字元集混亂。

MySQL資料庫讀寫分離中間件Atlas

圖3 通路架構

如圖3所示,用戶端發來SET NAMES UTF8語句,Atlas從連接配接池裡取出一個空閑連接配接(連接配接1),在連接配接1上執行該語句後,将連接配接1放回連接配接池。用戶端再發來一條SELECT語句,Atlas又從連接配接池裡取出一個空閑連接配接(連接配接2),連接配接2大機率與連接配接1不是同一個連接配接,在連接配接2上執行SELECT語句,而連接配接2并未設定UTF-8字元集,是以造成亂碼。

MySQL資料庫讀寫分離中間件Atlas

圖4 通路架構

解決方案:Atlas記錄每個用戶端和每個連接配接的目前字元集狀态(以一個正整數表示即可)并進行相關修正。當用戶端發來SQL語句時,Atlas會檢查該語句是否要設定字元集(SET NAMES或SET CHARACTER_XXX)。如某個用戶端發來SET NAMES UTF8,Atlas從連接配接池内取出連接配接1并執行該語句,執行成功後,将該用戶端和連接配接1的目前字元集均置為UTF-8。之後該用戶端發來SELECT語句(或其他非SET類語句),Atlas從連接配接池内取出連接配接2,然後比較用戶端的目前字元集(上步已設定為UTF-8)和連接配接2的目前字元集(假定為GBK),若一緻則直接執行,若不一緻則在SELECT前插入一條SET NAMES UTF8,将兩條語句一起發給MySQL,第一條語句起到将連接配接2的字元集修正為UTF-8的作用,然後再執行SELECT則不會亂碼。當然也可以進一步優化,就是由Atlas直接傳回SET類語句的結果(OK)給用戶端,省去一次與MySQL的互動過程。

自驗證

MySQL資料庫讀寫分離中間件Atlas

圖5 連接配接認證

圖5是MySQL的連接配接認證示意圖。

  1. Connect階段,用戶端建立與MySQL的TCP連接配接;
  2. HandShake階段,MySQL向用戶端發送握手包,其中攜帶多項資訊,如服務端的特性标志、字元集等,此處我們主要關心的是其包含的20個位元組的随機串;
  3. Auth階段,用戶端将握手包内的随機串取出,與自身的密碼一起執行以下運算SHA1( password) XOR SHA1( “20-bytes random data from server” SHA1( SHA1( password ) ) ),得到一個加密串,加上用戶端的使用者名、特性标志、字元集等附加資訊,組成認證包發給MySQL。
  4. Auth-Result階段,MySQL通過在mysql.user中儲存的該使用者名對應的密碼,也執行同樣的運算得到一個加密串,并與認證包中的加密串對比,若相同則認為密碼正确,傳回結果OK給用戶端,否則傳回ERR拒絕連接配接。

傳回OK後應用層連接配接即建立完畢,接下來用戶端向MySQL發送SQL語句,并從MySQL接收語句的執行結果,然後再發送下條語句……直至有一方斷開連接配接為止。

MySQL資料庫讀寫分離中間件Atlas

圖6 Atlas連接配接通路工作原理

圖6是1.x版本的Atlas的工作過程。

可以看到,在連接配接認證階段,Atlas簡單地透傳用戶端與MySQL雙方的通訊包。初看似乎并沒有問題,但考慮到Atlas後面挂接的是一主多從多台MySQL時,情況就變得比較複雜。

用戶端向Atlas建立連接配接時,Atlas需要選擇轉而向哪台MySQL建立連接配接。以最簡單的一主一從為例:若Atlas向主庫建連接配接,則從庫上沒有連接配接,無法實作讀寫分離;若向從庫建連接配接,則問題更嚴重,因為主庫上沒有連接配接,是以寫語句無法執行。那麼能不能同時向兩台MySQL建連接配接呢?問題在于兩台MySQL都會發送握手包,而用戶端遵循MySQL協定,它不可能一次接受兩個握手包……

在1.x中,我們使用了一個取巧的辦法,即在主庫上預留一定數量的連接配接,比如32個。用戶端向Atlas建立連接配接時,首先檢查主庫上的連接配接數是否已經達到32個,若未達到則向主庫建連接配接,若已達到則向從庫建連接配接。這樣既保證了寫語句可以在主庫上執行,又使得讀語句可以在從庫上執行,進而正确實作讀寫分離。這個辦法有幾個潛在的問題:1. 若有多于32個用戶端同一瞬間發送寫語句,則主庫上連接配接數将不夠用,當然也可以讓某些用戶端阻塞等待其他用戶端讓出連接配接來部分解決;2. 無法支援長連接配接;3. 在主庫連接配接數達到32個之前,仍然無法讀寫分離。

仔細分析以上情況,可以得知,其本質原因是Atlas需要依賴用戶端的連接配接動作轉而向MySQL建連接配接,是以能建立連接配接的數量最大不超過用戶端的Connect次數,這就極大地制約了Atlas作為中間件的靈活性。為了擺脫這一束縛,就要求Atlas能自主決定何時、向哪台MySQL、建立多少個連接配接。實作該特性的前提是,Atlas必須知悉用戶端的使用者名和密碼,因為從1.x的互動圖上可知,Atlas透傳用戶端的認證包給MySQL,而認證包内隻有加密串而沒有密碼明文,也不可能反解(否則我們就破解了MySQL的加密協定)。

MySQL資料庫讀寫分離中間件Atlas

圖7 Atlas 連接配接通路工作原理

圖7是2.x版本Atlas的工作過程。

使用者名和密碼事先已經作為配置在Atlas啟動時加載進來(當然也可以通過Atlas的管理接口進行動态的增删改查)。當用戶端來連接配接Atlas時,Atlas自行産生一個握手包(包含20位元組随機串)發給用戶端,然後接收用戶端發來的認證包,根據配置的使用者名和密碼進行加密計算并檢查結果,根據結果正确與否傳回OK或ERR給用戶端,進而完成用戶端的連接配接認證工作。用戶端發來SQL語句時,Atlas檢查連接配接池中是否有空閑連接配接,若有則直接使用,若沒有則根據配置的使用者名和密碼自行向MySQL建立新連接配接,然後供該語句使用。

此方案的優點:真正完全的讀寫分離,支援長連接配接,連接配接按需建立,連接配接數随并發度上升而增加,随并發度下降而減少(可以依靠MySQL的wait_timeout,也可以由Atlas監控連接配接的空閑時間)。

小結

從以上内容可以看出,MySQL中間件産品的設計與開發需要對相關協定有比較深入的了解,且因MySQL資料庫軟體自身的更新,協定還可能随之改變。限于篇幅,還有不少功能點不能一一細述,有興趣者可查閱項目源碼托管平台上的資源。

SDCC 2017·上海站将于2017年3月17-19日登陸申城,三大技術峰會和24位嘉賓,彙聚國内知名的網際網路公司CTO、架構師、技術總監,暢談運維、資料庫和架構的熱門話題和技術熱點,遇見精益運維發起人&優維科技CEO王津銀、MongoDB大中華區首席架構師唐建法和華為軟體API開放平台架構師李林鋒等大牛。截止3月5日前門票八折優惠中,5人以上團購立減400元,詳情點選注冊參會。