中商惠⺠交易中臺架構演進:對 Apache ShardingSphere 的應⽤

SphereEx發表於2022-04-21

翟揚,中商惠民平臺組架構師,參與中臺建設 0 到 1 的過程,主要負責交易中臺、商品中臺、搜尋平臺的建設和研發工作。

快消品⾏業傳統的運營模式,已經不能適應新時代的發展要求,單⼀推動供應鏈末端的升級,⽆法實現社群商業的⾼質量發展。中商惠民繼續以百萬家社群超市訂貨服務為業務核⼼,從服務零售終端門店轉型升級,向推動快消品全產業鏈數字化轉變,構建 B2B2C 資料閉環運營體系,為品牌商、經銷商、社群超市提供採、銷、營、配全產業鏈轉型升級解決⽅案,持續推動業務探索和創新。這⼀切,強⼤的中臺系統功不可沒。

交易中臺介紹

中商惠民歷經⼏次技術迭代,2016 到 2017 年開始 PHP 技術棧轉到 Java 技術棧並進⾏微服務化,2018 年開始打造中臺。

2021 年年初完成的交易中臺⼀期上線,2022 年 3 ⽉完成⼆期上線。

伴隨著業務成長和多樣性的變化,要及時響應新需求,同時要避免對現有業務產⽣影響,本著提⾼⼈效,降低成本的原則,中商惠民推出了強中臺發展戰略。對系統⽽⾔,要降低系統之間的耦合度,增加可擴充套件性,提取業務共性的同時要保證低延遲。在 2021 年之初完成了對之前 OMS 的重構,推出了以訂單的交易過程為核⼼的交易中臺項⽬,分為以下四個核⼼模組:訂單狀態流轉系統,管理系統,訂單履約系統,訂單費⽤計算系統。同時,基於業務分層,又拆分為訂單查詢系統,報表系統,管理後臺等。

在系統的重構過程中避免技術需求對業務需求產⽣阻礙,上線過程分為兩個階段:應⽤系統拆分和資料拆分。

系統拆分

作為業務“重災區”的管理系統,在業務快速成長的過程中,頻繁地對系統作出調整還需要適配多個業務線,會把研發同學搞的焦頭爛額,疲憊不堪……基於這些問題,制定以下原則對系統進⾏拆分重構:

  1. 避免不同業務線處理邏輯耦合,互相牽制和影響,降低邏輯複雜度。

  2. 根據訂單⽣命週期中職責進⾏劃分,保證每個系統領域獨⽴職責單⼀。

  3. 進⾏讀寫分離,保證核⼼功能的穩定性,避免頻繁迭代影響核⼼功能。

  4. 引⼊ ElasticSearch,解決外接索引問題,降低資料庫索引壓⼒ 。


資料拆分

資料拆分的⽬的是降低單表維護壓⼒,資料量達到千萬級別時,資料庫表的索引和欄位維護都會對線上環境產⽣不⼩的影響。決定對資料表做拆分時,需要⾯臨以下⼏個問題:

  1. 制定拆分原則:什麼樣的表屬於資料密集型?拆分的數量怎麼定?

  2. 資料讀寫問題:如何解決表拆分後的讀寫問題?

  3. 上線⽅案:如何做到平滑上線?

拆分原則

以 3 年作為⼀個迭代週期,拆分數量=((每⽉新增的資料量* 36)+ 歷史資料量)/單表資料量上限

根據我們維護阿⾥雲 RDS MySQL 的經驗來看,基於在使⽤ Innodb 引擎下,進⾏ DDL 操作時,⼩於 500 萬資料量,執⾏時間⼤概⼏⼗秒;⼤於 500 萬,⼩於 1000 萬資料量,執⾏時間⼤概 500 秒左右;⼤於 1000 萬資料量,⼩於 5000 萬左右,執⾏時間⼤概 1000 多秒;⼤於 5000 萬以上資料量,執⾏時間在 2000 秒以上。

單表分配上限可以取決於單表操作對業務帶來的影響,當然也有⼀些⽅案可以解決 DDL 操作鎖表的問題,例如雙表同步,切換表名的⽅式可以將影響降低為秒級。

庫表的拆分數量最好是 2 的 N 次⽅,有利於後期做⽔平擴容。

技術選型

資料表拆分之後,需要解決資料雜湊和查詢問題。作為⼀家中⼩型企業,⾃研⼀套分庫分表的中介軟體成本過⾼,初期還會⾯臨各種踩坑的風險,合理的⽅案是採⽤⼀套開源的資料庫分庫分表的中介軟體。

Apache ShardingSphere

Apache ShardingSphere 是⼀套開源的分散式資料庫解決⽅案組成的⽣態圈,由 JDBC、Proxy 和 Sidecar(規劃中)這 3 款既能夠獨⽴部署,又⽀持混合部署配合使⽤的產品組成,均提供標準化的資料⽔平擴充套件、分散式事務和分散式治理等功能,可適⽤於如 Java 同構、 異構語⾔、雲原⽣等各種多樣化的應⽤場景。

Apache ShardingSphere 5.x 版本提出 Database Plus 概念。Database Plus 是一種分散式資料庫系統的設計理念,透過在碎片化的同構或異構資料庫之上搭建使用和互動的標準層和生態層,併疊加擴充套件更多計算能力,例如資料分片、資料加解密等,使得所有應用和資料庫之間的互動面向 Database Plus 構建的標準層,從而遮蔽資料庫碎片化對上層業務帶來的差異化影響。

新版本開始致⼒於可插拔架構,項⽬的功能元件能夠靈活的以可插拔的⽅式進⾏擴充套件。⽬前,資料分⽚、讀寫分離、資料加密、影⼦庫壓測等功能,以及 MySQL、PostgreSQL、SQL Server、Oracle 等 SQL 與協議的⽀持,均透過外掛的⽅式織⼊項⽬,開發者能夠像使⽤積⽊⼀樣定製屬於⾃⼰的獨特系統。Apache ShardingSphere ⽬前已提供數⼗個 SPI 作為系統的擴充套件點,仍在不斷增加中。

選擇 Apache ShardingSphere 有以下⼏點理由:

  1. Apache ShardingSphere 功能符合預期,可以解決⽬前的問題並且有豐富的擴充套件性。

  2. Apache ShardingSphere 社群活躍度⾼,遇到問題會有專⼈及時響應。

  3. 公司採⽤ SpringCloud 技術棧,整合⽅便,成本低。

  4. 效能表現參考官⽅⽂檔符合預期,完全可以⽀撐現有業務。

ShardingSphere ⽀持以下 3 種模式:


公司⽬前服務端技術棧只涉及 Java 語⾔,暫時不需要考慮異構的場景,出於對靈活性、程式碼侵⼊性和部署成本⽅⾯的綜合考慮,最終選⽤ ShardingSphere-JDBC 的實現⽅案。

技術實現

過程中最複雜的部分其實不是外掛整合部分,⽽是上線環節。

上線過程避免被業務團隊“投訴”,需要儘可能規避對終端⽤戶產⽣的影響,做到⽆感知並且可回滾,最終我們將上線過程拆解為以下⼏步,如圖所示:


下⾯來解釋⼀下這⼏步過程:

第⼀步:處理全量資料的過程,需要保證新⽼庫的資料保持⼀致。因為涉及到資料分⽚策略,⽬前市⾯上沒有⽀持資料分⽚策略的同步⼯具,這塊我們⾃研了⼀個資料同步⼯具,⽀持配置分⽚策略。

第⼆步:處理增量資料,保持新舊庫的資料⼀致性。此處我們的實現⽅案是採⽤開源元件 canal 來監聽資料庫的 binlog,將資料庫修改同步到訊息佇列,再由資料同步⼯具監聽訊息寫⼊新庫。架構⽅案並不複雜,但是這個環節要注意的是資料⼀致性問題,增量資料不能丟資料,同時要控制單⾏資料的順序寫⼊,防⽌出現 ABA 的問題。

第三步:處理讀流量灰度上線。根據公司業務的敏感度,進⾏灰度上線,測試讀流量是否正常,直⾄完成所有的讀流量切換到新庫的過程。此時我們服務的狀態對於資料庫來講就是⼀個寫⽼庫,讀新庫的狀態。此環節要注意的是業務對資料⼀致性的敏感度問題,因為⽼庫到新庫⼤概是秒級的延遲,在部分對⼀致性要求較⾼的敏感場景需要考慮讀⽼庫。

第四步:將應⽤的所有讀流量切到新庫上,保持寫⽼庫讀新庫的狀態。

第五步:處理寫流量的過程,為了考慮降低程式的複雜度,沒有考慮灰度釋出的場景,當然灰度的⽅案也是可以做到的,此處不過多展開。我們的做法是將新庫資料回寫到⽼庫,來⽀持回滾策略,然後⼀次釋出將所有流量統⼀切到新庫。(此⽅案風險較⾼,可能會對⼤⾯積⽤戶產⽣影響,儘量在測試環境進⾏充分評估,需要關注程式碼覆蓋率和效能測試是否達標。)

⾄此就完成了整個釋出上線的過程,由於藉助了 ShardingSphere-JDBC 中介軟體對 SQL 改寫和結果歸併的能⼒完成整個改造的成本不⾼,對程式碼⼏乎沒有侵⼊性,上線過程也⽐較平滑。

另外還有⼀些在接⼊ ShardingSphere-JDBC ⽅案⾥可能需要注意的問題。

  1. ShardingSphere-JDBC 對於資料插⼊邏輯⾥有個預設處理,在 SQL 內不存在分⽚列進⾏資料插⼊操作時,會導致每個分⽚表都插⼀條相同的資料。

  2. ShardingSphere-JDBC 對於批次的 SQL 語義分析時,只會解析第⼀條 SQL 的邏輯表,會導致執⾏報錯。這個問題已經反饋給官⽅,近期應該會修復此問題。另外官⽅也提供了詳細的 SQL 示例,詳細列舉了哪些在⽀持的範圍內,開發之前需要詳細的閱讀⽂檔。

  3. ShardingSphere-JDBC 連線模式的選擇。

從資源控制的角度看,業務⽅訪問資料庫的連線數量應當有所限制。它能夠有效地防⽌某⼀業務操作過多的佔⽤資源,從⽽將資料庫連線的資源耗盡,以至於影響其他業務的正常訪問。特別是在⼀個資料庫例項中存在較多分表的情況下,⼀條不包含分⽚鍵的邏輯 SQL 將產⽣落在同庫不同表的⼤量真實 SQL,如果每條真實 SQL 都佔⽤⼀個獨⽴的連線,那麼⼀次查詢⽆疑將會佔⽤過多的資源。

從執⾏效率的角度看,為每個分⽚查詢維持⼀個獨⽴的資料庫連線,可以更加有效的利⽤多執行緒來提升執⾏效率。為每個資料庫連線開啟獨⽴的執行緒,可以將 I/O 所產⽣的消耗並⾏處理,為每個分⽚維持⼀個獨⽴的資料庫連線,還能夠避免過早的將查詢結果資料載入⾄記憶體。獨⽴的資料庫連線,能夠持有查詢結果集遊標位置的引⽤,在需要獲取相應資料時移動遊標即可。

以結果集遊標下移進⾏結果歸併的⽅式,稱之為流式歸併,它⽆需將結果資料全數載入⾄記憶體,可以有效的節省記憶體資源,進⽽減少垃圾回收的頻次。當⽆法保證每個分⽚查詢持有⼀個獨⽴資料庫連線時,則需要在復⽤該資料庫連線獲取下⼀張分表的查詢結果集之前,將當前的查詢結果集全數載入⾄記憶體。因此,即使可以採⽤流式歸併,在此場景下也將退化為記憶體歸併。

⼀⽅⾯是對資料庫連線資源的控制保護,⼀⽅⾯是採⽤更優的歸併模式達到對中介軟體記憶體資源的節省,如何處理好兩者之間的關係,是 ShardingSphere 執⾏引擎需要解決的問題。具體來說,如果⼀條 SQL 在經過 ShardingSphere 的分⽚後,需要操作某資料庫例項下的 200 張表。那麼,是選擇建立 200 個連線並⾏執⾏,還是選擇建立⼀個連線串⾏執⾏呢?效率與資源控制又應該如何抉擇呢?

針對上述場景,ShardingSphere 提供了⼀種解決思路。它提出了連線模式(Connection Mode)的概念,將其劃分為記憶體限制模式(MEMORY STRICTLY)和連線限制模式(CONNECTIONSTRICTLY)這兩種型別。

記憶體限制模式

使⽤此模式的前提是,ShardingSphere 對⼀次操作所耗費的資料庫連線數量不做限制。如果實際執⾏的 SQL 需要對某資料庫例項中的 200 張表做操作,則對每張表建立⼀個新的資料庫連線,並透過多執行緒的⽅式併發處理,以達成執⾏效率最⼤化。並且在 SQL 滿⾜條件情況下,優先選擇流式歸併,以防⽌出現記憶體溢位或避免頻繁垃圾回收情況。

連線限制模式

使⽤此模式的前提是,ShardingSphere 嚴格控制對⼀次操作所耗費的資料庫連線數量。如果實際執⾏的 SQL 需要對某資料庫例項中的 200 張表做操作,那麼只會建立唯⼀的資料庫連線,並對其 200 張表串⾏處理。如果⼀次操作中的分⽚散落在不同的資料庫,仍然採⽤多執行緒處理對不同庫的操作,但每個庫的每次操作仍然只建立⼀個唯⼀的資料庫連線。這樣即可以防⽌對⼀次請求對資料庫連線佔⽤過多所帶來的問題。該模式始終選擇記憶體歸併。

記憶體限制模式適⽤於 OLAP 操作,可以透過放寬對資料庫連線的限制提升系統吞吐量;連線限制模式適⽤於 OLTP 操作,OLTP 通常帶有分⽚鍵,會路由到單⼀的分⽚,因此嚴格控制資料庫連線,以保證線上系統資料庫資源能夠被更多的應⽤所使⽤,是明智的選擇。

我們發現在記憶體限制模式下,過程中會因為 MySQL 的 innodb 引擎的 cache buffer 載入策略導致操作變為 io 密集型,導致 SQL ⼤量超時,解決此問題的辦法就是在不變更資料庫資源的情況下我們程式多⼀層處理,如果發現沒有分⽚鍵的時候,先在外接索引中確認⼀下分⽚鍵,再透過分⽚鍵來進⾏資料庫檢索。

價值收益

  1. 效能提升

透過架構重構,有效控制單表資料量,⼤幅縮減慢 SQL,下降將近 50%。

  1. 節省研發資源,降低成本

引⼊成熟的 Apache ShardingSphere ⽆需重新開發分表元件,降低了研發和踩坑的成本,研發同學只需要集中精⼒處理業務問題。

  1. 豐富的擴充套件性

Apache ShardingSphere 對於資料加密,分散式事務,影⼦庫等⽅⾯都具備良好的擴充套件性。

寫到最後

在紐曼(Sam Newman)的《微服務設計》⼀書中曾經寫到:“與建造建築物相⽐,在軟體中我們會⾯臨⼤量的需求變更,使⽤的⼯具和技術也具有多樣性。我們創造的東西並不是在某個時間點之後就不再變化了,甚⾄在釋出到⽣產環境之後,軟體還能繼續演化。對於我們創造的⼤多數產品來說,交付到客戶⼿⾥之後,還是要響應客戶的變更需求,⽽不是簡單地交給客戶⼀個⼀成不變的軟體包。因此架構師必須改變那種從⼀開始就要設計出完美產品的想法,相反我們應該設計出⼀個合理的框架,在這個框架下可以慢慢演化出正確的系統,並且⼀旦我們學到了更多知識,應該可以很容易地應⽤到系統中。” Apache ShardingSphere 正是這樣⼀款極具潛⼒的產品,未來⼀定可期。

歡迎新增社群經理微信(ss_assistant_1)加入交流群,與眾多 ShardingSphere 愛好者一同交流。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70001955/viewspace-2888005/,如需轉載,請註明出處,否則將追究法律責任。

相關文章