適配混合云,貨拉拉的數據庫中間件建設之路
林靜
JAVA 技術專家
- 貨拉拉技術中心核心基礎設施部Java技術專家,對數據庫中間件研發有深刻的理解和豐富的實戰經驗。
- 曾任職摩托羅拉子公司UniqueSoft Java專家,主導自動逆向工程系統Java方向研發; 曾任職阿里本地生活中間件技術專家,負責DAL中間件的研發,同時負責多活體系中全局控制中心和數據層的建設。
今天的分享主要包含以下幾個方面的內容:
一直以來,都有這樣的觀點——隨著云時代的到來,基礎設施都會被云接管,基礎設施都沒了,自然也就沒必要自建中間件了。 而我的觀點恰恰相反,正是云時代的到來,我們才更需要自建中間件。
為什么這么說呢,主要有這樣兩個原因:一個是技術適配問題,我們的原生環境和云環境不可能是完全一致的,想要業務平滑上云就必然要由中間件做適配;另一個更重要的原因是保持廠商中立,各個云廠商的服務并沒有統一的標準,一旦企業習慣了某些獨一無二的服務,企業也就失去了自由選擇云平臺的能力。所以自建中間件是云時代的必然之路。而自建數據庫中間件正是其中不可或缺的一環—— “適配混合云,數據庫中間件的建設之路”,正是這次我要和大家探討的主題!
一、背景
- 業務體量持續增長遇到單庫瓶頸;
- 業務線不斷增多,數據庫總量不斷膨脹;
- 多語言異構,技術底座不斷演進,新老服務過度;
- 多云混合云部署。
和大多數上升期的企業一樣,隨著業務蓬勃發展, 技術底盤也迎來了自己的挑戰。 從量變到質變,很多原來習以為常的解決方案變得不再那么完美,DB就是一個典型的例子。 原先單個DB毫無壓力的好日子不見了,在高并發、海量數據的壓力下,DB無論是吞吐量還是存儲都開始遇到瓶頸。 而當DB在高水位的時候,也是最容易出故障的時候。
隨著業務不斷豐富和多元化,原來的單體架構也在向微服務架構演進,而PHP為主的技術棧也由于各方面的考慮向Java技術棧過渡。在DB層面表現出來的就是大量的拆庫、遷庫、建庫工作。熟悉這塊的同學都知道“拆庫、遷庫”是一個耗時耗力又高風險的操作。
而混合云背景,又加碼了問題的復雜度。混合云意味著我們的架構設計必須具有跨云特性,需要能適配不同的云環境,不依賴特定的云廠商的獨有廠品。從業務研發視角看,就是統一的解決方案,不需要重復開發的代碼來適配底層環境。
二、混合云下的問題與挑戰
- 各云廠商的RDS存在各種差異 ;
- 現有線性DB擴容方案不能跨云 ;
- DB頻繁拆分合并,風險高容易影響業務 ;
- DB層產品變更成本高,無法及時享受新技術紅利。
剛才介紹了背景,我們把內容再捋一捋。
RDS在一個云平臺一般有多種選擇,而多云的情況可能就有一種逛淘寶的感覺了。一般來說協議上會兼容mysql,但具體到某個特定SQL語法,主從部署方案等細節的時候,就會有各種細微的差別。比如我們要限制所有SQL刪除數據必須有where條件,有的產品不支持這個功能,有的產品可能支持動態修改,而有的產品只能通過重啟來修改。
剛才我們提到單機瓶頸,云廠商都提供了各自的解決方案。早期的云廠商會提供自己的數據庫中間件,比如阿里云的DRDS,通過分庫分表的方式來支持數據庫的水平擴展。經過一段時間的沉淀和演進,現在云廠商推出了數種新型的分布式數據庫產品,提供了更好的可運維性,我們不用關心部署的細節,而直接享受它線性擴展的能力。這些新產品在單云環境下都是非常合適的解決方案,而問題是這些方案并不能跨云,這些方案都依賴各個云廠商的獨有產品。
還有一個問題是變更。我們就以拆庫為例子,兩個業務公用一個數據庫,現在需要拆分出來。那么具體執行順序怎么安排呢?
① 同步數據到新庫;
② 業務中斷;
③ 業務服務使用新串重啟;
④ 業務恢復。
這個過程中我們依賴業務具有自我中斷的能力,另外我們會長時間中斷業務,需要確定是不是每一個業務節點都重啟了,否則就是數據不一致的災難。萬一出問題需要回滾,這個冗長的步驟又要再來一次。這個時候我們就會想,我們要是能夠靈活的控制連接串,動態設置它指向哪里的DB就好了。一把就全切過去,有問題馬上切回來,中間業務服務都不用重啟。
最后我們再關心一下成本問題。云廠商由于其強大的技術背景以及市場競爭壓力,總能推陳出新,不斷推出產品的性價比更高的新產品,來替代老產品。我們對低廉的價格很感興趣,但變更的風險又讓我們不敢輕舉妄動。
三、混合云下數據庫中間件建設
怎么解決上述問題呢?核心是解耦和適配。怎么理解呢?我們遇到的大部分問題都是底層的RDS和業務服務高耦合,這種情況在過去其實非常合理的架構模式,而在云時代已經不能滿足我們對可運維性,高可用的要求。因此我們需要通過自建數據庫中間件來解耦業務服務和數據層,同時由數據庫中間件來適配DB層,給業務服務提供統一一致的體驗。
從這張圖,我們可以看到借由數據庫中間件,業務層和DB層實現了解耦。這樣的架構下,DB層的變得更靈活也更安全。我們可以靈活地選擇DB層的產品,甚至做出跨云的設計。
回到剛才的DB拆分問題,在新的架構下,我們就可以這樣處理:
- 業務服務使用新串(指向老庫)重啟;
- 同步數據到新庫;
- DB流量中斷;
- 流量指向新庫;
業務服務只需要修改鏈接串,而不用再做任何其他的操作。中斷時間變得非常短,只要完成增量數據同步就可以恢復數據庫的訪問。同時回滾操作也在DBA團隊閉環了,原本需要幾方配合的變更,變成了DBA團隊內部閉環的動作,大大降低了復雜度,無論是效率,還是安全程度,都得到了非常大的提升。
1、核心功能與特性
前面說解耦是由自建數據庫中間件帶來的架構靈活性來解決的,那么適配又是怎么做的呢?適配主要體現在數據庫中間件的具體功能上,我們在設計實現數據庫中間件功能的時候就需要有這樣的考量。下面我們來詳細講講這幾個功能點:
1)模版式分庫分表,讀寫分離
分庫分表可以是說目前性價比最高的數據庫線性擴容方案,主流的分布式數據庫中間件都是采用這樣的方案。
- 提供給業務研發的視角是一個使用的視角。站在業務研發的角度,這是一個可伸縮的新型mysql數據庫,DB容量可以隨著業務的發展按需擴容。
- 提供給DBA的視角是一個運維的視角。這是一個由分庫和分表組成的集群,根據實際DB壓力來調節分庫分表的數量。
我們可以發現無論是業務研發視角,還是DBA視角,都是比較直觀的,不會引入特別抽象的概念。一條SQL實際執行其實是非常復雜的,其中有語法解析,分庫分表規則映射,新SQL生成、新SQL下發到分庫執行,結果聚合等等。這些復雜過程雖然重要,但從使用者的角度不是價值而是負擔,因此我們把細節全部交給數據庫中間件來隱藏起來。
我們給業務研發同學交付的是和普通DB一樣的連接串。我們給DBA同學提供的是新建邏輯庫的API,參數是分庫分表的數量,而具體的哈希函數選擇,分表分布等全部提供了默認模版。這里正是企業自建數據庫中間件的特點,我們不會考慮做一個需要用戶自己調參的大而全的產品,而是迎合自身業務的特色直接給出一個最佳實踐的直觀產品。
這樣的設計是非常靈活的。可以遇見不遙遠的未來,會出現一種公認高性價比且穩定的新型數據庫,我們完全可以把邏輯庫的實現替換掉,而業務服務無需重啟直接享用。
2)支持多租戶,資源利用更高效
多租戶是一個云時代中間件必備的能力。多租戶核心在于資源池化,按需共享,能夠非常有效的提高資源利用率,簡單來說就是省錢。
多租戶這個特性可以幫助數據庫中間件適應各種使用場景。比如在DEV環境,我們可以用一個數據庫中間件節點來代理所有的測試DB,使業務研發在測試開發階段保持和生產一致的體驗,而完全不用擔心資源浪費問題。
多租戶在設計實現上,主要需要考慮兩個方面的隔離:一個是配置隔離,不同邏輯庫的動態配置變更應該互不影響;另一個是資源隔離,不同邏輯庫的SQL執行不應該爭搶資源。
3)SQL鏈路追蹤與SQL治理
SQL鏈路追蹤在一個企業級的環境中是非常實用且重要的功能,抓到一條問題SQL后,能夠迅速定位具體來源,將大大提升線上排障效率,加快故障恢復,減小損失。這個能力顯然不是單獨一個中間件能搞定的,而是需要和一個成熟的技術底盤結合起來才能實現。
這里需要一提的是,和所有的中間件一樣,數據庫中間件產品只有成為企業技術體系的一部分才能發揮全部的價值,即可以獲得包括監控,配置中心,注冊中心的支持,也給整個技術體系提供DB層的埋點數據和治理能力。而成為一個數據孤島的話,肯定會有功能和運維性的短板,甚至成為企業技術體系里的黑洞。
- SQL治理能力包括: 緊故障防護能力、應急故障處理能力;
- 故障防護能力包括:消峰限流、連接管理、SQL攔截審計等;
- 應急故障處理能力包括:指定SQL限流攔截、SQL Index hint注入、強制綁定主庫等;
整套SQL治理能力工具集能夠適配各種云環境,有效保障DB健康穩定。
2、核心技術指標
這里列了一些比較關鍵的指標,我們來簡單解讀下:
- 線性擴容上限——單機容量*1024
- 多租戶上限——單集群理論上限達10K
這兩個指標主要考量這樣一個問題:我們現在的架構方案是否能夠滿足企業未來2-3年的發展,假設未來3年我們的體量翻10倍,我們的架構能否夠平穩應對。從數據層這個角度來講,答案是肯定的,以自建數據庫中間件為基礎的數據庫層架構,可以平穩支撐未來百倍的業務增長。
- DB遷移影響 — 秒級中斷
DB遷移影響這個指標代表的是我們的遷移能力的成熟度。也可以這么說,它代表在業務無損的前提下,我們遷移能力的自由度。秒級中斷的能力,可以幫助我們做到業務低峰期無損遷移。最理想的情況肯定是全天可操作,這也是我們努力的方向。
- 低延遲—99.999%延遲小于10ms
低延遲指標其實包含了2個維度的考量:一個RT平均值的小,一個RT抖動的小。做為數據庫中間件,穩定性肯定第一位的,我們一般選擇RT抖動做為衡量穩定性的核心指標。在技術上我們選擇了Netty NIO的多路復用框架以及Java16 ZGC來實現這個維度指標的提升。
- 高可用—99.999%服務可用
高可用的量化指標一般用X個9來表達,X個9表示在系統1年時間的使用過程中,系統可以正常使用時間與總時間(1年)之比。一般企業級軟件要求是4個9,也就是全年允許掛50分鐘,5個9的話就是全年只能掛5分鐘,而我們的數據庫中間件上線16個月一直保持0故障,算是我們做的比較滿意的部分。
- 低成本— 成本占用不到RDS的5%
所謂低成本,自己做的蛋糕肯定比買的便宜。當然這只是開個玩笑,這個肯定不能這么考慮。這里涉及到三個成本,軟硬件成本+運維成本+研發成本。前兩個相信大家都比較熟悉了。
我主要分享下對第三個成本的觀點:和過去大家什么都想要自己開發相反,現在我們往往會過分夸大自研投入的成本,而直接放棄自研的選項。在中間件領域開源氛圍濃郁的大環境下,我們其實比以往更容易培養中間件人才,利用社區資源我們也更有把握解決相關的難題,因此研發的成本并不會太高。而我們在研發實踐后也能夠反饋社區,形成正循環,這是一個雙贏選擇。
3、可運維性與技術設計
可運維性是衡量一個中間件產品設計是否“好用”的重要維度。雖然是針對產品上線后的生命周期,卻是在設計階段決定。可運維性的內容比較豐富,在總結反思我們產品的建設過程后,總結了以下4點。
1)面向故障設計
- 非核心依賴可降級
- 核心依賴做好冗余
- 建設系統“自證清白”能力
- 最后一道防線手動SOP
面向故障設計應該是大家比較熟悉的概念了。特別是在體量上來以后,故障就變成是必然,只有一臺機器的時候我們說今天它有可能壞,而我們有10000臺的時候,我們會肯定的說今天必然要壞一臺。特別值得一提的是,我們有時候會神化云環境的穩定性,以為上云了就不會有故障了。云環境的穩定性不是說不出故障,而是有一套完整機制來故障恢復,是一種反脆弱的設計。而有些故障特別是硬件故障,并沒有快速的恢復辦法,比如交換機壞了,光發現問題就需要不短的時間,更何況恢復。所以無論是什么環境,我們都要設計好故障的預案。
在數據庫中間件的場景里,可降級的依賴一般指影響旁路的功能,動態修改配置,監控等。不可降級的部分包括關鍵數據,硬件設備等。對于數據我們肯定要做好備份,而硬件問題換個角度其實更多是單節點故障,集群化是非常合適的解決方案。
在一個大的系統里發生故障,往往是到處都冒煙,但就是找不到真正出問題的點。這個時候“自證清白”就顯的非常重要,如果所有組件能快速確定自己的狀態,故障點清晰可見了。當然“自證清白”并不是那么容易的,真實的情況往往是大盤故障了,所有人都摸不清自己的模塊是不是有問題,然后各種看日志,查監控,可即使這樣的付出,還是很難得出結論。“自證清白”是一個重要又非常有挑戰的能力,需要對本領域有非常深的理解,同時又需要監控報警等基礎能力配合。在數據庫中間件這里,主要關注內部的工作線程負載,外部表現的RT,以及網絡的丟包這三個關鍵數據。
手動SOP屬于兜底手段了,比如數據庫中間件保留了本地文件啟動能力。
2)產品標準化
- 針對不同使用特征,分級群隔離
- 使用統一硬軟件標準
- 盡量復用企業現有標準
- 接入標準化
- 功能簡單正交
標準化是一種追求全局最優的設計理念。
一個是功能設計的標準化。 作為基礎中間件,我們的功能必須是簡單正交的,這樣就能夠在實現功能的時候更專注保證質量,同時也留出進一步的編排的空間。舉個例子,我們提供了限制指定SQL pqs的功能,也提供了上報異常SQL(類似SQL長度過長)的能力。這個時候如果在上層控制面做一個自動限制異常SQL的能力,就會非常順利,即容易實現也可以方便的灰度回滾。而我們如果一開始就在數據庫中間件這里憋大招,就很難做到這樣自如,反而很有可能會因為功能相互影響,復雜度太高,引入bug等破壞迭代節奏。
另一個是外部依賴的標準化。 比如盡量使用公司統一的ECS機型,能夠最大的避免機器沒庫存的問題。使用公司統一的基礎組件,能夠免去額外維護的成本,同時提高的產品開發效率和穩定性。
最后是用戶使用的標準化。 我們應該避免提供太多的不確定給用戶,而是只給一個標準答案。我們應該統一出一套最佳實踐來,在這個基礎上做好配套的優化。這樣用戶可以輕松接入,并把這個過程輕松復制給其他人。提升效率的同時,也避免了因不合理的使用姿勢可能引發的故障。
3)排障智能化
- 服務監控,系統監控,外部依賴監控
- 鏈路追蹤
- 自動報警
監控的重要性毋庸置疑,我們需要在一開始就把監控埋點納入到開發計劃中,而不是在某次故障后,才開始補充各種監控指標。
監控埋點的要點在于能夠全面覆蓋產品主要流程,包括數據流和控制流,正常流程和異常流程。這個時候容易進入的誤區是,過早的對埋點數據做處理。比如內部有一個工作隊列,比起根據某個閾值打一個隊列是否繁忙的點,更合適的做法是直接打出隊列長度,這樣我們可以使用圖表來展示隊列的繁忙度變化,也可以靈活的設置告警。告警可是0值的空閑異常告警,也可以是100的繁忙異常告警。
無論是報警,鏈路還是監控都是要服務于排障的,所以我們要站在的方便排障的角度,來設計和驗收這些指標埋點。當我們懷疑某處的打點是否必要時,可以看看是否對線上排障有幫助。
線上排障我們說要“智能化”,其實我們真正想做到的是“傻瓜式”的排障預案。能夠在故障發生第一時間收到報警,然后通過鏈路追蹤工具直接找到根因,最后通過預案按部就班處理。
4)管理自動化
- 最終“消滅”人工環節
- 用戶自助
自動化提升效率是我們的共識,自動化能夠幫助我們從大量的重復勞動中解放出來,提升工作效率。
那么如果量不是那么大,也不是那么重復的部分呢?是否還有必要自動化,比如審核等動作。
我認為是必要的。當我們發現某個部分不能自動化,想用人工的方式“糊弄”過去的時候,往往這就是我們沒有考慮清楚的地方,不徹底解決這個問題,這個問題就會像狗皮膏藥一樣一直拉低我們的用戶體驗。我們之前就遇到過這樣情況:新建邏輯庫可能會影響老邏輯庫,怕用戶填錯信息導致異常,我們就設置了好幾道人工審核來review。而事實上人工審核并沒有解決問題,反而阻礙了整體的自動化。最后我們重新設計了邏輯庫的配置隔離和回滾能力,自然自動化也不成問題了。
“消滅”人工環節很大程度是在掃清我們產品的潛在問題,是我們假裝看不見的地方。
用戶自助也是類似的,一個能夠自助的系統意味什么呢?意味我們的系統是健壯穩定的,我們有良好的隔離設計,有強大的容錯能力。把自助當成目標,是建設更好的產品的良好推動力。
總的來說,可運維性影響軟件后期所要投入的“隱性成本”,是一種高回報的長遠的投資。
四、解決的問題及收益
這里總結下自建數據庫中間件帶來的收益。
- 提升研發效率;
- 提升運維效率;
- 提升系統穩定性;
- 保持廠商中立,實現“云自由”。