關於分散式資料庫和UDDB, 你需要知道的一些事(下)

前言

在上一篇文章中,基於資料庫三角這個模型,我們介紹了資料庫近幾十年以來的發展歷史,並討論了在分散式資料庫領域,目前碰到問題。最後,我們深入討論了基於中間件的分散式資料庫解決方案,闡述UCloud基於中間件來做分散式資料庫的理由和思路。在這篇文章里,我們將給大家詳細介紹,過去一年中,UCloud基於中間件打造的分散式資料庫:UDDB(UCloud Distributed DataBase)的主要產品和功能特性。

本文將主要包括以下幾個內容:1. 基於 UDB 和 ULB 的架構設計2. 水平擴容,一鍵完成 2.1 傳統擴容方式的弊端 2.2 解決方案3. 讀寫分離功能,業內最好用 3.1 業界常見做法 3.2 讀寫分離一攬子支持4. 垂直分表和水平分表完美結合 4.1 業務拆分需求 4.2 UDDB的解決方案5. 優於傳統中間件的 SQL 支持 5.1 DDL語句支持 5.2 業內最好的聚合 Select 的支持 5.3 水平分表建表語法,返璞歸真6. 極簡的業務接入流程:如何不停服從 UDB 遷移數據? 6.1 業務數據遷移流程 6.2 遷移進度查詢 6.3 撤銷正在執行的遷移任務

1. 基於UDB和ULB的架構設計

UDDB 的底層存儲,要充分復用 UDB ,這是我們在研發 UDDB 時確定的首要原則,也是風險最低,能夠最大程度防止系統風險,保護業務的方法。

目前,NewSQL 是資料庫領域的技術熱點。這說明過去十幾年來,業內在 NoSQL 方向的嘗試,並不成功,最後還是覺得 SQL 和關係模型最靠譜。但是,從零開始打造一款存儲類的產品,需要很長時間,才能達到工業強度,真正用來承載客戶業務。而時間不等人,當越來越多的 UCloud 客戶需要分散式資料庫時,我們必須想辦法儘快滿足,但前提是風險足夠可控。

基於上述考慮, 我們選擇了資料庫中間件加 UDB 產品,來構建 UCloud 分散式資料庫。資料庫中間件經過過去十幾年的積累,技術上足夠成熟,已經是業內使用最多最廣的分散式資料庫解決方案。UDB 產品經過多年的運營和迭代,不僅產品本身品質量過硬,還打造了一支高效專業的運營和 DBA 團隊,7×24值守線上系統。

UDDB 的最終方案,直接復用高可用 UDB,作為存儲節點,確保底層存儲無單點和高可用,而存儲節點下面掛載的只讀實例,則復用了普通 UDB 節點。我們認為,這是保障業務數據可靠性和系統穩定性的最優選擇。同時,充分復用 UDB 來做存儲,也意味著 UDDB 開發團隊,可以將精力聚焦在中間件上,集中精力打造精品。

值得一提的是,中間件是無狀態的,數據都在 UDB ,即使中間件出現了災難性的bug,只要數據在 UDB 中不丟失,就可以通過分析業務+中間件日誌,來還原客戶數據。這是從頭開發的 NewSQL 產品無法做到的。

同時,我們使用了 ULB 來做中間件節點的雙活和負載均衡。ULB 同樣為 UCloud 的口碑產品,穩定,性能好,能夠迅速應對後端節點的異常,在過去幾年經受住了各種業務的檢驗。

總的來說,通過復用 UDB 和 ULB,穩妥又低成本地解決了數據存儲和中間件節點的容災和負載均衡問題,這體現了基於公有雲平台來開發產品的優勢:通過復用成熟的標準化產品,項目組只需要專註於核心模塊的開發,通過搭積木的方式,搭建出產品整套架構。

UDDB 總體架構圖如下:

------------------------------------------------UDDB總體架構圖-------------------------------------------------

2. 水平擴容,一鍵完成

UDDB 六大功能特性中,自動化的水平擴容,是我們最想做的功能。使用傳統的資料庫中間件,擴容依賴手工進行,操作複雜、過程漫長同時風險又高。這其中的痛苦,搞過的人都深有體會。我們先來看一下,傳統的水平擴容的有哪些步驟:

操作複雜: 一個擴容操作,涉及到5個步驟,諸如部署新節點、遷移數據、修改路由規則等操作,需要嚴謹的操作和細心的驗證。這其中又以遷移數據這一步,最為繁瑣(想像一下將某資料庫節點下面的一半數據,按照遷移規則,遷移到新的節點,最終要保證一條數據都不能錯)。

過程漫長:遷移數據過程涉及到幾十GB乃至幾百GB數據,在不同資料庫節點間的導入導出,遷移完成之後還需要對數據的正確性進行驗證,這些都是極其消耗時間的操作。

高風險:擴容過程涉及到架構的變更和數據的改動,配置文件的一個小紕漏, 或者遷移/驗證腳本的一個小問題,都可能導致整個流程回滾甚至數據丟失。

在 UDDB 這個產品中,我們設計了一個極簡的交互流程: 只需要點擊一個按鈕,即可完成全部水平擴容操作:

在這個按鈕背後,是精心編寫並嚴格測試的,近5000行代碼,將手工擴容操作各步驟進行了自動化。在水平擴容期間,UDDB 不停服,所有資料庫表均能正常訪問(只是在每遷移一張子表到目標 UDB 節點後,UDDB 會有幾毫秒到零點幾秒的訪問中斷)。

UDDB還提供一個 `中止水平擴展` 的按鈕, 在水平擴容操作啟動後,如果用戶想撤銷,還可以點次按鈕取消此次擴容:

3. 讀寫分離功能,業內最好用

前面提到,水平擴容是 UDDB 產品特性中,我們最想做的功能,那麼讀寫分離,則是最需要做的功能。通過回訪 UDB 客戶,我們發現很多客戶的業務數據量並不大,一個UDB 實例能夠容納,但是讀遠大於寫,以至於最高配的UDB實例都無法支持。

讀寫分離,是業內解決這個問題的常見方法。 具體的做法是添加幾個從庫,在主庫和從庫之間建立好複製關係,構成一個讀寫分離組;然後修改業務邏輯,將部分或者全部讀請求分流到從庫,通過從庫來分擔讀請求的壓力。

但這需要一定的開發和運維工作量:需要部署從庫,配置複製關係,還有修改業務程序,對讀請求做路由。雖然目前 UDB 產品,可以通過管理頁面,一鍵創建從庫並配置主從複製關係,但是業務程序的修改,依然無法避免。

在客戶回訪當中,我們發現不少客戶,希望 UCloud 提供對讀寫分離問題的一攬子支持。為此,UDDB 的一個核心目標,就是完美支持讀寫分離,做到好用且功能強大。下面是我們的方案:

1.讀寫分離集群創建:在創建 UDDB 實例頁面中,存儲節點選擇一個,並指定掛載到存儲節點下的,只讀實例的個數,即可創建一個一主多從的讀寫分離集群:

2.業務訪問: 業務訪問該讀寫分離集群,如同訪問一個單機資料庫,不需要做任何修改。由UDDB的中間件節點,負責識別業務的讀請求,然後分流到只讀實例。如果業務中包含事務,則事務下的所有讀寫請求,都路由到主節點。

3.讀請求分流比例配置: UDDB 默認的讀請求分流策略,是均勻分流,即將讀請求均勻分攤到主節點和從節點。同時,UDDB 還提供了其他三種分流策略,總共四種。這四種分流策略,可在管理頁面進行配置:

1.存儲節點: 選擇該配置項,中間件會將所有讀請求,都發往主節點,而從節點不接收任何讀請求;

2.均衡:選擇該配置項,中間件會將讀請求,均勻分流到主節點和各從節點,這也是UDDB默認的分流方式

3.只讀均衡:選擇該配置項,中間件會將讀請求,均勻分流到各從節點,而主節點不接收任何讀請求

4.自定義:選擇該配置項,能夠自由配置分流到每個節點的讀請求比例。

總之,UDDB 為讀寫分離場景提供了極簡的系統創建方式, 同時提供了四種讀請求分流配置策略,可以覆蓋絕大部分業務的需求。系統創建極簡,讀請求配置功能強大,這是對於讀寫分離問題,UDDB能夠給到客戶的支持。

另外,對於有多個存儲節點的 UDDB,亦可以提供讀寫分離。可以在各存儲節點後面,再掛載若干只讀實例,將本來落到該存儲節點的讀請求,分流到只讀實例。

4. 垂直分表和水平分表完美結合

當業務的數據量,達到單機資料庫能夠承載的上限,則需要做數據拆分。通過回訪使用UDB 的客戶,我們發下,業務數據拆分需求,分兩種情況:

a. 垂直分表:業務的增長,導致數據總量達到了,單個 UDB 節點的存儲容量上限,為此需要將一部分表,遷移到新的 UDB 節點,但客戶希望所有資料庫表的訪問地址,仍然是同一個。

b. 水平分表:業務的增長,導致某張大表的數據總量,達到單個 UDB 節點的存儲上限,為此需要增加新的 UDB 節點,並將這張表進行水平拆分,將其數據均勻劃分到各UDB 節點。

傳統的中間件,有的要麼只支持水平分表,但是不支持垂直分表; 有的兩種都能夠支持,但是對垂直表的 SQL 語句,有限制,其 SQL 支持範圍,等同於水平分表。比如,無法支持垂直表的複雜 join 和嵌套子查詢等。

而 UDDB 能夠做到:

a. 垂直分表和水平分表同時支持。如果客戶提交的建表語句, 是普通的建表語句,則 UDDB 將會選擇在第一順位的 UDB 節點創建該表; 客戶也可以指定在某個 UDB 節點上,創建該表,例如:

CREATE TABLE `t2_common` ( `id` int(11) NOT NULL, `price` int(11) NOT NULL, `name` varchar(64) DEFAULT NULL, `dt` varchar(32) DEFAULT NULL) ENGINE=InnoDB DEFAULT CHARSET=latin1on udb node udbha-vh3jmr;

b.垂直表 SQL 的支持程度,和單機 MySQL 基本一致。包括支持複雜的 join 和嵌套子查詢等。示例:

SELECT st.create_dept as dept_id, count(*) as total FROM db1.special_task st right join db1.alarm_event as ae on ae.event_uuid like concat(zxjc\_4\_,st.create_dept) or ae.event_uuid like concat(zxjc\_4\_,st.id,\_%) right join db1.alarm_action_status_history as ash on ash.risk_event_id = ae.id and ash.old_status in (2010,2020) and ash.new_status = 2020 and ash.change_time >= 2013-01-01 and ash.change_time <= 2013-12-31 WHERE st.create_dept = 34 and st.type = 22 and st.create_time >= 2013-01-01 and st.create_time <= 2013-12-31;

對於水平表,SQL的支持程度,現在還受限於跨節點join和分散式事務這兩個問題,和目前主流中間件基本保持一致。包括:

1.支持單個分片的Insert/Update/Delete/Repalce語句

2.支持單表的select語句,支持複雜的group by、order by、limit和集函數

3.支持分片規則一致的兩個表的Select join語句

5. 優於傳統中間件的SQL支持

傳統的資料庫中間件以 SQL 作為業務訪問介面,但對SQL支持力度不夠。一直以來,這點為用戶所詬病,也是 NewSQL 抨擊中間件的一大理由(即使傳統中間件事實上支持了90%以上的項目)。

若仔細分析各類 SQL 的語法特點,及客戶業務場景,可以看到,中間件模式,在 SQL 支持上並非天生羸弱。 當然,中間件模式下,SQL 支持的提升,並非一蹴而就,而是需要長期的打磨,作為一個公有雲服務,UDDB 將以快速迭代的方式,持續完善對 SQL 的支持。在本節先總結下,目前 UDDB 對 SQL 的支持,相較傳統中間件完善的地方。

5.1 DDL語句支持

用戶可以使用標準的 MySQL 客戶端,或者業務程序通過標準的 MySQL api,向 UDDB 提交 DDL 語句, 使用方式如同單機 MySQL 。

5.2 業內最好的聚合 Select 的支持

聚合 Select 語句支持(語句中包含Group by、Order by、Distinct、集函數、Limit等指令), 一直是傳統資料庫中間件的弱項。如果您手上剛好有資料庫中間件,可以測試下該軟體,對下面這條SQL語句的支持:

select distinct id, avg(price) from t1 where id>=1 group by concat(id,name) order by avg(price) limit 10;

我們的測試下來,發現不少資料庫中間件無法支持該 SQL 。或者語法錯誤,或者 SQL 的解釋邏輯出錯,或者返回結果不正確。

在我們看來,實現對聚合 Select 100%的支持,是必須的。傳統的資料庫中間件,對聚合類 SQL 支持不夠,是因為這些中間件往往來源於互聯網項目,而這類項目對聚合Select 使用較少。但 UDDB 的設計目標,是通用的分散式資料庫,不限定用戶業務場景,因此應該盡量提高 SQL 支持能力,對聚合 Select 的支持,是我們走出的第一步。

為此,我們花費不少時間,設計了一套完整的,全面覆蓋聚合 Select 的演算法,來做到對聚合 Select 100% 的支持。

所以,對於上面給出的 SQL , UDDB 是支持的。不僅如此,UDDB 還支持更多複雜的聚合 Select 。 您可以實際開通一個 UDDB 實例,來進行相關的測試。

5.3 水平分表建表語法,返璞歸真

業內各大基於中間件構建的分散式資料庫,都有自定義的水平分表建表語法,這些語法或者簡單清晰,或者全面而有繁複,都各有自己的特點,但總讓人感覺缺少點什麼。

而 UDDB 的水平分表的建表語法,和 MySQL 水平分區表建表語法幾乎完全一致(只是在關鍵字前面增加字元:U):

CREATE TABLE t1(uid INT NOT NULL, dt DATE)UPARTITION BY HASH(uid) UPARTITIONS 8;CREATE TABLE t3(id INT NOT NULL ,price int NOT NULL)UPARTITION BY RANGE(id)USUBPARTITION BY HASH(price)USUBPARTITIONS 4(UPARTITION p1 VALUES LESS THAN (100000),UPARTITION p2 VALUES LESS THAN maxvalue);

我們之所以這樣做,是因為在深入考察了其他基於中間件的分散式資料庫的建表語法後,發現不管是從功能的全面性和簡潔度, 還是從客戶的接受度上,各分散式資料庫使用的語法,其實都沒有超過 MySQL 官方的水平分區表建表語法。

一方面,MySQL 這套建表語法,提供了Hash、Key、List、Range四種數據劃分方式,而且 Hash、Key 和 List、Range 還可以複合,進行二次劃分,這就給水平分表提供了足夠的靈活度;同時,四種基礎的劃分方式簡單易懂,而組合之後又功能強大,充分體現了 SQL 語法設計方面的功力(當然,MySQL 這套並非自創,而是沿用自 Oracle ,在客戶介面上,這也可能 MySQL 為數不多的,模仿 Oracle 的功能點之一)。

另一方面,MySQL 建表語法更有傳播度和客戶基礎,網上也有很多資料可供學習。因此,與其自己造輪子,不如尊重 MySQL 和客戶習慣,直接復用大家熟悉的語法。

唯一的問題,是 MySQL 水平分區表建表語法的實現,稍顯複雜,但為了達到最好的客戶體驗,我們選擇了把這套建表語法規則全部實現了一遍(除了不支持 KEY 劃分方式外)。

6. 極簡的業務接入流程:如何不停服從 UDB 遷移數據?

一款存儲產品,除了要有好用的功能和優良的品質,也需要方便客戶遷入業務,否則客戶的使用成本就會比較高。客戶甚至會因為不方便遷入業務數據這個原因,放棄使用。

為了讓業務更好地接入 UDDB ,我們提供了一條遷移指令,通過這條指令,可以在不停服的情況下,將業務數據,從 UDB(包括運行在 UHost 上的自建MySQL實例)遷移到 UDDB 。遷移期間,業務仍然訪問原有的 UDB 實例,一旦遷移完成,業務的全部訪問請求,將被切換到 UDDB 。

6.1 業務數據遷移流程

假如客戶購買了一個 UDB 實例,或者在 UHost 上自建了一個 MySQL 實例,(為敘述方面,下面統一稱其為 UDB 實例),且在上面部署了業務。當這個 UDB 實例容量或性能達到極限時,客戶可以購買一個 UDDB ,來做一個分散式資料庫的解決方案。 購買並創建 UDDB 實例後,要將業務接入到 UDDB ,總共只需要做四步操作:

1.制定基於 UDDB 的數據存儲和訪問方案(垂直分表、水平分表、讀寫分離),並且確保業務的所有 SQL,包括對事務的要求,UDDB 都能夠支持。在少數情況下,業務中會有部分 SQL , UDDB 不能支持,此時可以考慮調整下業務程序的 SQL 或程序邏輯,或聯繫 UDDB 團隊來啟動迭代進行支持。

2.根據第一步制定的方案,在 UDDB 實例上,創建業務的資料庫表。

3.向 UDDB 提交數據遷移命令,將數據從 UDB 遷移到 UDDB 。遷移指令的示例如下:

create udb_import_task(src_udb_id:"udbha-k1wtyz",src_udb_addr:"10.19.5.157:3306",src_udb_user:"root",src_udb_passwd:"liuly624@cloud",import_dbs:"import1,import2",notes:"1st create trans task in bjd");

該命令一旦提交成功,則意味著 UDDB 內部做了兩件事情:

a. 修改了 UDDB 中間件節點的路由信息,後面所有訪問 import1,import2 這兩個庫的請求,發到 UDDB 後,都將被轉發到原來的UDB節點

b. 開始從 UDB 遷移數據到 UDDB

4.修改業務的 MySQL 訪問地址,由 UDB 切換到 UDDB 。此後,業務發往 UDDB 的請求,都將通過中間件, 被路由到原來的 UDB 。

如此,UDDB 一邊做數據遷移,一邊將 SQL 請求,路由到原來的 UDB 。一旦數據遷移完成,UDDB 將修改路由表,將 SQL 請求,路由到 UDDB ,最終完成將整個業務接入到 UDDB 的流程。

6.2 遷移進度查詢

還可以通過下面命令,查詢遷移進度:

其中,Status 的取值如下:

const ( UDB_IMPORT_TASK_NOTSTART = 0 UDB_IMPORT_TASK_ROUTETBL_SET = 1 UDB_IMPORT_TASK_COMPLETE = 2 UDB_IMPORT_TASK_FAILED = 3 UDB_IMPORT_TASK_CANCEL = 4 UDB_IMPORT_TASK_CANCELED = 5 )

當 Status 變為2時,意味著遷移完成,同時,業務發往 UDDB 的請求,都被路由到了UDDB 下面的 UDB 節點。此時,可以將原來的 UDB 節點下線。

6.3 撤銷正在執行的遷移任務

如果對遷移任務進度或者其他能力不夠滿意,還可以通過以下命令,中止遷移任務。中止後,遷移任務會停止,但是中間件仍然將請求路由到原有的 UDB 節點。客戶可以將業務的MySQL訪問地址,修改回 UDB 節點,最終完成撤銷。

drop udb_import_task(src_udb_id:"udb-88", import_dbs:"import1,import2");

結語

以 UDB 為主打產品的 UCloud 雲資料庫團隊,成立以來,一直專註於 OLTP 場景下的結構化數據存儲服務,幾年來不斷努力,力求精益求精,止於至善。OLTP 場景下的結構化數據存儲,看似平淡無奇,不似大數據領域中,有琳琅滿目的各種技術和產品,也不如機器學習領域充滿未來感和想像力,但要做好,要保證每一條數據都不丟失,每一次請求都不出錯,為客戶的業務提供最基礎的數據保障,需要紮實的研發和運維基本功,需要踏踏實實地從客戶需求痛點出發,去提煉需求,研發產品,規範運營,不斷優化。

延續著 UDB 產品的氣質,UDDB 五大功能特性,依然是圍繞 OLTP 場景下,客戶在海量數據存儲和處理中的痛點來打造,目標是切實有效地解決客戶的問題。我們並不追求前沿的,高大上的技術,只是懷著嚴肅的態度,本著為客戶業務負責的想法,基於目前廣泛使用的中間件技術和穩定的 UDB、ULB 產品,來構建 UDDB 。 總得來說,我們做了兩方面的事情:

  1. 通過利用公有雲的優勢,為客戶提供傳統中間件缺失的功能點。比如支持 DDL ,不停服水平擴容,以及業內最好用的讀寫分離;

  2. 加入了一些微創新,來擴展中間件的功能,不斷提高對業務的支持度。比如垂直分表和水平分表無縫結合,返璞歸真的水平分表語法,不停服遷移 UDB 節點數據等。

最終的目標,是構建一個如同單機資料庫一樣對業務友好,運行穩定,而又方便管理和運維的分散式資料庫,有效解決 UCloud 客戶在使用單機資料庫時遇到的容量和性能問題。


推薦閱讀:

Google Spanner 是一個什麼樣東西?對未來會產生什麼樣的影響?
innodb的意向鎖有什麼作用?
TiDB Best Practice
Redis 和 MongoDB 哪個更占內存?
新浪SAE中的資料庫常量是如何實現的?

TAG:分布式数据库 | 数据库 | MySQL |