共計 4662 個字符,預計需要花費 12 分鐘才能閱讀完成。
本篇文章給大家分享的是有關 OLTP 場景下的數(shù)據(jù)分布式設計原則是怎樣的,丸趣 TV 小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著丸趣 TV 小編一起來看看吧。
前言
最近幾年做分布式項目,很多工作是關于 OLTP(聯(lián)機交易系統(tǒng))場景下數(shù)據(jù)分布式架構的,疫情期間正好整理下這方面的一些設計與實踐。為避免篇幅太長,本文分為設計篇和技術篇,設計篇主要偏向數(shù)據(jù)拆分的理論與方法,還有一些原則與經驗。技術篇則主要會介紹分庫分表中間件的設計與使用實踐,以及如何構建一個完整的分布式數(shù)據(jù)服務平臺。
一般來說做分布式架構,應用層是好做分布式的,因為往往都是無狀態(tài)的(或者通過將數(shù)據(jù)轉移到 DB、緩存、MQ 等方式來實現(xiàn)無狀態(tài)),只需在流量入口、即在應用前面加一個負載均衡即可(例如 Nginx、HAProxy、F5),這在大單體架構也多已具備。所以一般我們說分布式架構,一個重要的部分就是要做數(shù)據(jù)的分布式化。
傳統(tǒng)單體集中式架構
數(shù)據(jù)的分布式不像應用那么簡單,因為各節(jié)點的數(shù)據(jù)可能是不一樣的,需要進行路由、解決多副本一致性,甚至多寫沖突等問題。雖然實現(xiàn)方案復雜,不過數(shù)據(jù)的分布式本質上就兩種樸素思想:復制和分片。復制技術在傳統(tǒng)關系數(shù)據(jù)庫中也很常見,主要用來做主備、雙活,例如 MySQL Replication、Oracle DataGuard 等。分片在數(shù)據(jù)庫里也有對應產品。例如 MySQL Fabric、Oracle Sharding,但與復制相比,這些數(shù)據(jù)庫廠商對應的分片方案卻一直沒有被大眾廣泛接受。
在 NewSQL 數(shù)據(jù)庫中往往都內置了 sharding 機制,而且都基于 paxos、raft 算法來保證復制一致性,關于分庫分表與 NewSQL 方案對比選型,可參見我之前一篇文章《分庫分表 vs NewSQL 數(shù)據(jù)庫》。
在 OLTP 場景下,復制和分片思想應用在傳統(tǒng)關系數(shù)據(jù)庫上,有兩個更為人熟知的名字,分庫分表與讀寫分離。
分庫分表,就是對原來單一數(shù)據(jù)庫表進行拆分,是基于傳統(tǒng)關系數(shù)據(jù)庫實現(xiàn)分布式架構轉型的一個主要方式,因此首先第一個問題:
為什么拆分? 什么時候需要拆分?
容量、性能、橫向擴展、微服務
單機數(shù)據(jù)庫的存儲、CPU、內存等資源都存在上限瓶頸,當數(shù)據(jù)量、訪問量到達一定量級后,性能則會急劇下降,也就是說通過 scale up 這種垂直擴展的方式是一個上限的,而且成本是較高的。
如果要實現(xiàn) scale out 橫向擴展,就需要把原來一張表的數(shù)據(jù)拆分到多張物理庫表中存儲(水平拆分)。
另外如果是微服務架構,拆分后的服務歸屬不同的系統(tǒng),對應不同的數(shù)據(jù)庫,其實就已經進行了垂直拆分。
拆分方式有哪些?
1、垂直拆分
垂直拆分一般更加貼近業(yè)務的拆分方式,在做微服務時使用最多的就是這種方式,具體會根據(jù) DDD(領域驅動設計)技術或者業(yè)務能力進行拆分,一般有界上下文確定了,拆分規(guī)則也就比較明確了。
這種方式對應用侵入性較小,往往只需要配置各自獨立數(shù)據(jù)庫 (可能是物理機,也可能只是不同的實列) 即可,最多做一個多數(shù)據(jù)源選擇的數(shù)據(jù)訪問層。
另外還有一種垂直拆分的場景是由于冷熱數(shù)據(jù),同一行數(shù)據(jù)的不同列訪問頻率差別很大,或者是有些 Text、Blob 等大字段影響讀寫效率,這時也會將這些列拆分到不同表中。這種方式一般不常見,很多時候是在做性能優(yōu)化時會考慮。
垂直拆分
垂直拆分的優(yōu)點:
拆分后業(yè)務清晰,拆分規(guī)則明確。往往是按照系統(tǒng)或者交易的
系統(tǒng)之間整合或擴展容易
數(shù)據(jù)維護簡單、架構復雜度低
垂直拆分的缺點:
部分業(yè)務表無法 join,只能在應用層通過接口方式解決
受每種業(yè)務不同的限制存在單庫性能瓶頸
往往會產生分布式事務場景
由于垂直切分是按照業(yè)務的分類將表分散到不同的庫,所以有些業(yè)務表會過于龐大,存在單庫讀寫與存儲瓶頸,這時就需要水平拆分來做解決。
2、水平拆分
水平拆分更加技術化,將一張表的數(shù)據(jù)分布到多張庫與表中,具體方式可分為:只分庫、只分表、分庫又分表。例如 order 表,只分庫(ds1.order、ds2.order hellip;dsk.order),只分表(ds.order_0、ds.order_1 hellip;ds.order_n),分庫又分表(ds1.order_0、ds2.order_1 hellip;dsk.order_n)。
水平拆分
水平拆分的優(yōu)點:
如果操作數(shù)據(jù)分布在同一庫中,可以支持 join、子查詢等復雜 SQL
解決了單庫性能瓶頸,支持橫向擴展
由于應用未拆分,如果有分布式數(shù)據(jù)訪問層,則應用改造較少
水平拆分的缺點:
拆分規(guī)則、分庫分表數(shù)量需要精心設計
如果涉及多個庫,會產生分布式事務場景
數(shù)據(jù)擴容時數(shù)據(jù)遷移工作量較大
跨庫 join 往往需要應用實現(xiàn),性能較差
數(shù)據(jù)合并、聚合、分頁等無法由數(shù)據(jù)庫直接支持
數(shù)據(jù)庫有分區(qū)表還要分庫分表嗎?
傳統(tǒng)關系數(shù)據(jù)庫的分區(qū)表本質上還是共享 cpu、內存,所以仍然面臨著 scale up 的問題,而且分區(qū)表支持的分區(qū)鍵往往也不夠靈活。但新的一些 NewSQL 分布式數(shù)據(jù)庫,如 OceanBase 的分區(qū)表分散在不同的存儲節(jié)點上,從而避免單機性能瓶頸問題。
拆分具體步驟
1、確定拆分方式
根據(jù)業(yè)務特性選擇合適的拆分方式,一般結合使用。
1)垂直拆分
場景:字段長度、訪問頻率差別較大字段表、微服務化
注意:需要在同事務中操作的表盡量不要做拆分
2)水平拆分
場景:數(shù)據(jù)量較大,超過單表、單庫性能
注意:是否有跨庫事務,是否有非分片鍵操作表的場景,會涉及到庫表掃描交易
2、確定拆分字段
1)垂直拆分表、字段
按照功能模塊進行拆分直接按表即可,如果是拆分部分列,則需添加關聯(lián)列甚至冗余列。
2)水平拆分字段
確保 拆分表都有分片鍵,多為主鍵或唯一索引,這些列中需包含分片信息。如果請求中未包含分片信息,則需要一個全局的路由表。
3、確定拆分規(guī)則
1)范圍 Range
適合按照一定規(guī)律有序遞增的業(yè)務字段,例如日期、流水 ID 等,這種方式,例如 0 -9999- 庫 1,10000~19999- 庫 2 hellip;;20150101-20161231- 庫 1,20170101-20171231- 庫 2 hellip;。
這種方式天然支持水平擴展,方便進行冷熱分離、歸檔,按需擴容方便,但負載容易不均衡,如果單庫壓力大,則也需數(shù)據(jù)遷移。
2)哈希 Hash
數(shù)據(jù)分布比較均衡,一般通過 mod 庫 / 表數(shù)量計算路由,本質上一種預分配,因此擴容時需要進行數(shù)據(jù)遷移,通常有一致性哈希、成倍擴容法。
3)應用自定義
由應用自定義路由規(guī)則,配置有分片 ID 對應的庫表序號,可以通過路由表、配置文件或其它自定義算法。這種方式靈活度最高,容易實現(xiàn)動態(tài)改變。
在我們項目中是 1、2、3 方式都有使用。
4、確定拆分數(shù)量
1)假設目標數(shù)據(jù)量為 T(根據(jù)業(yè)務發(fā)展需求預估)
2)單表數(shù)據(jù)量建議 P(例如 MySQL 為 500w),分表數(shù)量 =T/P
3)目前配置典型業(yè)務場景下,單庫性能穩(wěn)定前提下對應的數(shù)據(jù)容量上限 L
單庫性能可以根據(jù) cpu(80% 以上)、磁盤 IO(磁盤使用率 100% iowait 出現(xiàn)并逐步增大)、交易 tps 穩(wěn)定性 (出現(xiàn) tps 大幅度波動) 等系統(tǒng)指標確定其瓶頸狀態(tài)從而得到容量上限的評估。
4)分庫數(shù)量 =T/L
庫表的數(shù)量關系到未來擴容、以及運維需求,不宜太多也不宜太少,以上主要是從容量角度去計算,實際場景下還需要結合硬件成本預算、數(shù)據(jù)清理歸檔策略等因素綜合考慮。
拆分后怎么擴容?
1、垂直擴容
垂直拆分后,如果某個應用的數(shù)據(jù)庫壓力太大,可通過增加其資源配置 (CPU、內存、PCIE) 進行垂直擴容。
2、水平擴容
水平拆分下可以通過增加數(shù)據(jù)庫服務器進行擴容。這種方式需要進行數(shù)據(jù)遷移,如果一致性哈希則遷移就近節(jié)點數(shù)據(jù),如果是成倍擴容時則需遷移所有節(jié)點一半數(shù)據(jù)。
一致性哈希模式雖然遷移的數(shù)據(jù)量較小,但容易造成數(shù)據(jù)的冷熱不均,因此我們項目中采用的成倍擴容方式,具體方式是提前將表分出來,例如分成 128 張表,項目初期將這些表均勻分布在 4 臺數(shù)據(jù)庫服務器,隨著業(yè)務增加數(shù)據(jù)量增長,擴容到 8 臺數(shù)據(jù)庫,只需要將原 4 臺數(shù)據(jù)庫各自一半數(shù)量的表遷出到新增的 4 臺服務器,然后修改 SQL 路由即可。
成倍擴容:應對整體數(shù)據(jù)量增長,擴容后物理機是原有 2 倍
如果是單臺數(shù)據(jù)庫有熱點數(shù)據(jù)壓力,也可以只將該庫一部分數(shù)據(jù)遷移出新擴容的庫。
單庫擴容:應對某個切片數(shù)據(jù)增長過快,擴容到獨立的物理機
拆分后面臨的問題
引入分布式事務的問題
跨庫 Join 的問題
多庫合并排序分頁問題
SQL 路由、重寫問題
多數(shù)據(jù)源管理問題
多維度拆分后帶來的數(shù)據(jù)匯總查詢等操作問題
解決方式:
盡可能避免分布式事務、跨節(jié)點 join、排序場景
避免使用數(shù)據(jù)庫分布式事務,提供柔性事務支持(冪等、沖正、可靠性消息、TCC)
由應用層解決 join 問題
提供分布式數(shù)據(jù)訪問層
匯總庫、二級索引庫、小表廣播
關于分布式數(shù)據(jù)訪問層在技術篇進行詳細介紹。
讀寫分離
在實際業(yè)務場景中,對數(shù)據(jù)庫的讀寫頻率是不一樣的。有的是寫多讀少,例如交易流水表; 有的是讀寫均衡,例如訂單表; 有的則是讀多寫少,如客戶、信息以及配置等信息表。
數(shù)據(jù)分片解決的是單點性能瓶頸和橫向擴展能力,適合寫壓力比較大的場景。而讀多寫少的這類場景,如果單庫容量可以滿足,則可通過讀寫分離來解決讀壓力大的問題。具體可以把寫操作路由到主庫,讀操作按照權重、機房等分散在主庫和各個從庫。
讀寫分離
讀寫分離模式下需要注意幾點:
1)主從延遲。在從庫上讀比主庫數(shù)據(jù)有一定時延(一般在毫秒級別,寫壓力大時可能在秒級別),所以選擇這種方式時業(yè)務上要允許一定的數(shù)據(jù)時延,例如一般對外查詢類交易都使用這種方式。
2)同一事務中,不能在從庫讀取數(shù)據(jù),因為可能由于數(shù)據(jù)延時讀取到臟數(shù)據(jù),違背事務的一致性,所以必須在主庫讀取。在實際開發(fā)時,數(shù)據(jù)訪問層可根據(jù)是否關閉事務自動提交來自動判斷是否必須在主庫讀。
3)對于數(shù)據(jù)延遲容忍度很低的查詢交易,可以在開發(fā)時單獨再封裝一個從主庫查詢的接口,或者在入參增加“是否需要強一致”標志,交易實現(xiàn)時根據(jù)該標志選擇從主庫還是從庫讀。
在實際項目中分庫分表和讀寫分離方式都有場景在用,但注意一般情況下避免使用分庫分表 + 讀寫分離這種復雜方案,因為分庫分表后讀寫壓力也不會太大了。
原則與經驗
數(shù)據(jù)分布式是個系統(tǒng)工程,需要從領域建模、場景劃分、數(shù)據(jù)訪問、數(shù)據(jù)遷移擴容等多方面綜合考慮,在落地實現(xiàn)前要從全局做好設計,這里簡單列下我們的一些設計原則與經驗:
1)用簡單的方案解決問題。能不切分盡量不要切分,切莫為了分布式而拆分。讀寫分離能解決問題,就不分庫分表。
2)切分一定要選擇合適切分規(guī)則(能保證 90% 交易不會跨分片),梳理好所有場景,提前規(guī)劃好再實施。
3)數(shù)據(jù)訪問層設計上功能要強大,但一定明確使用場景,切忌無腦濫用。比如我們項目中數(shù)據(jù)訪問中間件雖然支持分布式事務 XA,但一般并不推薦使用; 支持 DDL,但聯(lián)機交易時禁止使用; 支持多庫鏈式事務提交,但默認只支持嚴格單庫事務。
4)制定應用開發(fā)規(guī)范,明確 SQL 使用限制與要求,SQL 要盡量簡單。例如我們項目使用 MySQL,部署在 PC Server 上,單機性能相比小型機上 DB2、Oracle 差很多,因此禁止使用觸發(fā)器、外鍵、join,SQL 操作必須攜帶索引與拆分列(數(shù)據(jù)訪問層也會校驗),主鍵必須是自增等等。
5)盡量使用柔性事務解決跨庫與跨系統(tǒng)事務問題。能用 MQ 最終一致性就別用 Saga、TCC。
以上就是 OLTP 場景下的數(shù)據(jù)分布式設計原則是怎樣的,丸趣 TV 小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注丸趣 TV 行業(yè)資訊頻道。