1. 程式人生 > >數據庫架構演變及分庫分表

數據庫架構演變及分庫分表

分庫分表 補償機制 消息隊列

當生產環境中業務量激增,數據庫數據量也會極具增加。當數據庫的數據量達到一定程度時(數據庫瓶頸),數據庫宿主機負載超高,會嚴重影響業務,嚴重時會導致數據庫宕機。
為了避免這種極端情況的發生,我們應當在發生前做好預案,用於解決數據庫數據量過載的問題。
以下是我個人工作中使用的解決方案:
1)數據庫主從或多主多從方案
2)數據庫冷熱數據拆分
3)數據庫分庫分表操作
4)在數據庫前端增加緩存redis或memcached

一開始時,公司部分業務的架構如下(全都是單節點情況)
技術分享圖片
經過自己強調該架構嚴重的缺點:節點單一,服務中斷情況嚴重等嚴峻問題,後演變為如下簡圖的架構:
技術分享圖片
上述架構中,我們更新代碼實現了不中斷服務,同時解決了部分單一節點問題。後來一段時間業務做推廣優惠活動,業務量激增,數據庫數據量成指數性增長。數據庫負載一直居高不下。當前的架構已經無法滿足業務需求,後又提出對數據庫進行數據的冷熱數據拆分和數據讀寫分離(可以考慮使用中間件Mycat、Atlas、MaxScale,也可以考慮讓開發寫路由規則,有些框架自帶路由規則),這裏我們使用Mycat,如下圖架構,這裏只顯示後端:

技術分享圖片
隨著時間的推移,單庫單表已經達到業務忍受的極限,這時候已經在硬件還是在mysql參數調優上都已經無法滿足業務。這時候提出了數據庫的分庫分表。這裏主要講解數據庫的分庫分表
分庫分表一共有兩種方案:
1) 垂直拆分
2) 水平拆分
1、 垂直拆分
所謂垂直拆分,就是將單一數據庫拆分成多個數據庫,可以考慮的方案:
1) 根據業務邏輯進行拆分
2) 根據冷熱數據進行拆分
這裏先講解根據業務邏輯拆分:
這裏以商品--訂單--用戶為例,當多種類商品存放於一個數據庫和一張表中時,隨著時間推移,數據量增大,單庫單表查詢能力下降,可以根據商品種類來進行拆分,比如公司的產品:高中、初中、小學,由單個表拆分成三個獨立表。如果還大,繼續拆分,高中拆分成高一、高二、高三。初中拆分成初一、初二、初三。小學拆分成小一、小二......小六。(所有的分庫分表都源於生活中的邏輯)。
我們生產環境中針對數據進行了拆分,冷數據(采用MyISAM引擎),熱數據(采用Innodb引擎),同時將MyISAM引擎的數據庫的宿主機多采用redis或memcache進行緩存,Innodb引擎的數據庫的宿主機針對業務情況分配計算型和內存型物理機,還是混合型,同時可以考慮使用redis做部分存儲,用於緩解數據庫壓力。
垂直拆分優缺點:
優點:
1) 由單庫單表拆分成多庫多表,降低了數據庫增刪改查壓力
2) 對冷熱數據拆分,降低了成本並合理利用硬件
3) 對於垂直拆分的數據庫,在設計數據庫時就應當考慮好數據庫架構的延展性,(否則會對後期的擴展造成很大的阻力)
4) 按照業務分庫後,業務邏輯更加清晰,更方便運維管理。
缺點:
1) 對於聯表查詢,帶來了不便,可以通過調用接口的方式來觸發聯表查詢的操作,對整個系統而言,復雜度提高了。
2) 對於有些數據庫,存在單庫性能瓶頸影響整個業務情況。
3) 同時對於事務而言,復雜度提高了。
2、 水平拆分
所謂水平拆分,簡單來說就是將一張表中的數據按照行拆分成多份存儲到不同的數據庫的表中。比如user表有9000條數據,將user表拆成user1、user2、user3三張表存放於不同的數據庫中,user1存儲前3000條數據,中間3000條數據存儲到user2表中,最後3000條數據存放到user3表中。
水平拆分的分片維度(有很多算法來決定采用哪種水平拆分的方案)
1) 按照哈希切片,對某個字段進行求哈希值,然後除以分片的數量,最後取模,取模相同的數據為一個分片,這就是哈希分片。
這種分片方式沒有時效性。對數據分散比較均勻,缺點是如果需要查詢則需要對數據進行聚合處理。
2) 按照時間序列算法切片,有的業務會有明顯的季度波動,可以使用時間算法。這種算法就是數據分配不均。
生產環境中,我們部分業務采用的是哈希算法和時間序列算法的混合式
說到水平拆分,不得不提的是水平拆分的路由規則
設計數據庫的時候,就要考慮到數據庫中各種表的路由規則,同時還需要考慮數據表將來按照什麽樣的路由規則來進行分庫分表。
比如,某個新用戶註冊,這個用戶是如何分配到哪個庫哪個表中?一般在註冊的時候都會系統自動分配一個uid。根據這個uid可以按照某種算法來進行分配。比如uid%4,(這裏將一張表分成4個表),如果是1 則分到第一張表中,以此類推。

水平拆分的優缺點:
1) 單庫單表的數據量最大就那麽大,有數據量的限制,我們可以根據業務情況來分配,剛好達到最大(這個量很難把握),既能提高該表的操作性能又節省了資源
2) 因為對表的結構改變非常少,對於開發而言更改代碼量非常少,只需要增加路由規則。
3) 對整體系統的穩定性和負載都有大大的提高
缺點:
1) jion操作非常困難,尤其是跨庫的聯表查詢
2) 有的拆分規則很難抽象出來
3) 分片的事務一致性比較難解決
4) 還有數據庫的擴容和維護比較困難
針對上述垂直拆分和水平拆分,都有以下缺點:
1) jion操作困難
2) 事務一致性困難
3) 多個數據源的管理變復雜。
如何解決事務一致性問題呢?(這個重要性排在第一位)
1) 采用本地事務的一致性(能用則用)
2) 分布式事務處理
分布式事務處理的解決方案:
1) 兩階段提交方案(最嚴格的方案,很少使用,因為是阻塞協議造成性能問題):分為準備(鎖定資源)和提交(消費資源)兩個階段。這種方式依賴資源管理器。
2) 最大努力保證模式(最常用,極端情況需要實時補償,將已提交的數據進行回滾)不依賴資源管理器
一共有兩種操作:從消息隊列中消費消息和更新數據庫操作
開始消息事務?開始數據庫事務?接收消息?更新數據庫?提交數據庫事務?提交消息事務
當更新數據庫時,突然中斷,會進行回滾,恢復到初始狀態,特殊情況,如果提交數據庫事務成功,但是提交消息事務失敗,就會造成消息再次消費的情況。這個可以通過消息冪等處理(有時候很多消息無法滿足冪等性比如update操作,可以考慮增加一個消息應用狀態表來記錄消息消耗情況和數據庫事務情況),出現上述極端情況,需要實時補償。這種補償機制類似於TCC模式Try-Confirm-Cancel(如果try都成功了,它會重復confirm或重復cancel,直到都成功),TCC要求Confirm/Cancel都必須是冪等操作。

3) 事務補償機制
前面兩種方案,都不是最好的,事務補償機既能保證性能又能盡最大可能保證事務的一致性。說白了就是突破事務一失敗就回滾的思想,指定在一定的時間內不斷提交直到成功為止,如果超時則回滾。
分庫分表過程中帶來的問題
1、 擴容和數據的遷移
數據已經分片,同時數據量已經快達到閾值,需要對集群進行擴容,都采用成倍擴容。
以下是5個擴容步驟:
1) 增加新的路由規則,對新的數據庫采用新路由規則進行寫,同時對舊數據庫采用舊路由規則寫。(雙寫,兩套路由規則)
2) 將雙寫前的舊數據按照新規則寫入到新數據庫中(需要做大量的數據清洗工作)。
3) 將按照舊的分片規則的查詢更改為按照新的分片規則查詢。
4) 將雙寫的路由規則代碼下線,只按照新規則寫數據
5) 刪除按照舊分片規則寫入的歷史數據。
2、 分庫分表帶來的聯表查詢問題
舉例:
賣家和買家,賣家需要查看該商品賣出情況,買家需要查看自己的交易情況,可以考慮使用兩個表,一個以買家維度,記錄買家商品交易情況,一個以賣家維度,記錄賣家該商品交易情況。也就是查詢交易和交易的數據是分別存儲的,並從不同的系統提供接口。
方法二:通過搜索引擎解決(大數據了)

參考文檔:
https://blog.csdn.net/it_man/article/details/21593187

數據庫架構演變及分庫分表