數據庫怎麼分庫分表

數據庫瓶頸

無論是IO瓶頸仍是CPU瓶頸,最終都會致使數據庫的活躍鏈接數增長,進而逼近甚至達到數據庫可承載的活躍鏈接數的閾值。在業務service來看, 就是可用數據庫鏈接少甚至無鏈接可用,接下來就能夠想象了(併發量、吞吐量、崩潰)。html

IO瓶頸

  • 第一種:磁盤讀IO瓶頸,熱點數據太多,數據庫緩存放不下,每次查詢會產生大量的IO,下降查詢速度->分庫和垂直分表
  • 第二種:網絡IO瓶頸,請求的數據太多,網絡帶寬不夠 ->分庫

CPU瓶頸

  • 第一種:SQl問題:如SQL中包含join,group by, order by,非索引字段條件查詢等,增長CPU運算的操做->SQL優化,創建合適的索引,在業務Service層進行業務計算。
  • 第二種:單表數據量太大,查詢時掃描的行太多,SQl效率低,增長CPU運算的操做。->水平分表。

分庫分表

水平分庫

一、概念:以字段爲依據,按照必定策略(hash、range等),將一個庫中的數據拆分到多個庫中。
二、結果:

  • 每一個庫的結構都同樣
  • 每一個庫中的數據不同,沒有交集
  • 全部庫的數據並集是全量數據
    三、場景:系統絕對併發量上來了,分表難以根本上解決問題,而且尚未明顯的業務歸屬來垂直分庫的狀況下。
    四、分析:庫多了,io和cpu的壓力天然能夠成倍緩解

水平分表

一、概念:以字段爲依據,按照必定策略(hash、range等),講一個表中的數據拆分到多個表中。
二、結果:

  • 每一個表的結構都同樣
  • 每一個表的數據不同,沒有交集,全部表的並集是全量數據。
    三、場景:系統絕對併發量沒有上來,只是單表的數據量太多,影響了SQL效率,加劇了CPU負擔,以致於成爲瓶頸,能夠考慮水平分表。
    四、分析:單表的數據量少了,單次執行SQL執行效率高了,天然減輕了CPU的負擔。

垂直分庫

一、概念:以表爲依據,按照業務歸屬不一樣,將不一樣的表拆分到不一樣的庫中。
二、結果:

  • 每一個庫的結構都不同
  • 每一個庫的數據也不同,沒有交集
  • 全部庫的並集是全量數據
    三、場景:系統絕對併發量上來了,而且能夠抽象出單獨的業務模塊的狀況下。
    四、分析:到這一步,基本上就能夠服務化了。例如:隨着業務的發展,一些公用的配置表、字典表等愈來愈多,這時能夠將這些表拆到單獨的庫中,甚至能夠服務化。再者,隨着業務的發展孵化出了一套業務模式,這時能夠將相關的表拆到單獨的庫中,甚至能夠服務化。

垂直分表

一、概念:以字段爲依據,按照字段的活躍性,將表中字段拆到不一樣的表中(主表和擴展表)。
二、結果:

  • 每一個表的結構不同。
  • 每一個表的數據也不同,通常來講,每一個表的字段至少有一列交集,通常是主鍵,用於關聯數據。
  • 全部表的並集是全量數據。 三、場景:系統絕對併發量並無上來,表的記錄並很少,可是字段多,而且熱點數據和非熱點數據在一塊兒,單行數據所需的存儲空間較大,以致於數據庫緩存的數據行減小,查詢時回去讀磁盤數據產生大量隨機讀IO,產生IO瓶頸。
    四、分析:能夠用列表頁和詳情頁來幫助理解。垂直分表的拆分原則是將熱點數據(可能常常會查詢的數據)放在一塊兒做爲主表,非熱點數據放在一塊兒做爲擴展表,這樣更多的熱點數據就能被緩存下來,進而減小了隨機讀IO。拆了以後,要想獲取所有數據就須要關聯兩個表來取數據。
    但記住千萬別用join,由於Join不只會增長CPU負擔而且會將兩個表耦合在一塊兒(必須在一個數據庫實例上)。關聯數據應該在service層進行,分別獲取主表和擴展表的數據,而後用關聯字段關聯獲得所有數據。

分庫分表工具

  • sharding-jdbc(噹噹)
  • TSharding(蘑菇街)
  • Atlas(奇虎360)
  • Cobar(阿里巴巴)
  • MyCAT(基於Cobar)
  • Oceanus(58同城)
  • Vitess(谷歌) 各類工具的利弊自查

分庫分錶帶來的問題

分庫分表能有效緩解單機和單錶帶來的性能瓶頸和壓力,突破網絡IO、硬件資源、鏈接數的瓶頸,同時也帶來一些問題,下面將描述這些問題和解決思路。算法

事務一致性問題

分佈式事務

當更新內容同時存在於不一樣庫找那個,不可避免會帶來跨庫事務問題。跨分片事務也是分佈式事務,沒有簡單的方案,通常可以使用「XA協議」和「兩階段提交」處理。
分佈式事務能最大限度保證了數據庫操做的原子性。但在提交事務時須要協調多個節點,推後了提交事務的時間點,延長了事務的執行時間,致使事務在訪問共享資源時發生衝突或死鎖的機率增高。隨着數據庫節點的增多,這種趨勢會愈來愈嚴重,從而成爲系統在數據庫層面上水平擴展的枷鎖。sql

最終一致性

對於那些性能要求很高,但對一致性要求不高的系統,每每不苛求系統的實時一致性,只要在容許的時間段內達到最終一致性便可,可採用事務補償的方式。與事務在執行中發生錯誤馬上回滾的方式不一樣,事務補償是一種過後檢查補救的措施,一些常見的實現方法有:對數據進行對帳檢查,基於日誌進行對比,按期同標準數據來源進行同步等。數據庫

跨節點關聯查詢join問題

切分以前,系統中不少列表和詳情表的數據能夠經過join來完成,可是切分以後,數據可能分佈在不一樣的節點上,此時join帶來的問題就比較麻煩了,考慮到性能,儘可能避免使用Join查詢。解決的一些方法:緩存

全局表

全局表,也可看作「數據字典表」,就是系統中全部模塊均可能依賴的一些表,爲了不庫join查詢,能夠將這類表在每一個數據庫中都保存一份。這些數據一般不多修改,因此沒必要擔憂一致性的問題。bash

字段冗餘

一種典型的反範式設計,利用空間換時間,爲了性能而避免join查詢。例如,訂單表在保存userId的時候,也將userName也冗餘的保存一份,這樣查詢訂單詳情順表就能夠查到用戶名userName,就不用查詢買家user表了。但這種方法適用場景也有限,比較適用依賴字段比較少的狀況,而冗餘字段的一致性也較難保證。服務器

數據組裝

在系統service業務層面,分兩次查詢,第一次查詢的結果集找出關聯的數據id,而後根據id發起器二次請求獲得關聯數據,最後將得到的結果進行字段組裝。這是比較經常使用的方法。網絡

ER分片

關係型數據庫中,若是已經肯定了表之間的關聯關係(如訂單表和訂單詳情表),而且將那些存在關聯關係的表記錄存放在同一個分片上,那麼就能較好地避免跨分片join的問題,能夠在一個分片內進行join。在1:1或1:n的狀況下,一般按照主表的ID進行主鍵切分。併發

跨節點分頁、排序、函數問題

跨節點多庫進行查詢時,會出現limit分頁、order by 排序等問題。分頁須要按照指定字段進行排序,當排序字段就是分頁字段時,經過分片規則就比較容易定位到指定的分片;當排序字段非分片字段時,就變得比較複雜.須要先在不一樣的分片節點中將數據進行排序並返回,而後將不一樣分片返回的結果集進行彙總和再次排序,最終返回給用戶以下圖:運維

上圖只是取第一頁的數據,對性能影響還不是很大。可是若是取得頁數很大,狀況就變得複雜的多,由於各分片節點中的數據多是隨機的,爲了排序的準確性,須要將全部節點的前N頁數據都排序好作合併,最後再進行總體排序,這樣的操做很耗費CPU和內存資源,因此頁數越大,系統性能就會越差。
在使用Max、Min、Sum、Count之類的函數進行計算的時候,也須要先在每一個分片上執行相應的函數,而後將各個分片的結果集進行彙總再次計算。

全局主鍵避重問題

在分庫分表環境中,因爲表中數據同時存在不一樣數據庫中,主鍵值平時使用的自增加將無用武之地,某個分區數據庫自生成ID沒法保證全局惟一。所以須要單獨設計全局主鍵,避免跨庫主鍵重複問題。這裏有一些策略:

UUID

UUID標準形式是32個16進制數字,分爲5段,形式是8-4-4-4-12的36個字符。 UUID是最簡單的方案,本地生成,性能高,沒有網絡耗時,可是缺點明顯,佔用存儲空間多,另外做爲主鍵創建索引和基於索引進行查詢都存在性能問題,尤爲是InnoDb引擎下,UUID的無序性會致使索引位置頻繁變更,致使分頁。

結合數據庫維護主鍵ID表

在數據庫中創建sequence表:

CREATE TABLE `sequence` (  
  `id` bigint(20) unsigned NOT NULL auto_increment,  
  `stub` char(1) NOT NULL default '',  
  PRIMARY KEY  (`id`),  
  UNIQUE KEY `stub` (`stub`)  
) ENGINE=MyISAM;
複製代碼

stub字段設置爲惟一索引,同一stub值在sequence表中只有一條記錄,能夠同時爲多張表生辰全局ID。使用MyISAM引擎而不是InnoDb,已得到更高的性能。MyISAM使用的是表鎖,對錶的讀寫是串行的,因此不用擔憂併發時兩次讀取同一個ID。當須要全局惟一的ID時,執行:

REPLACE INTO sequence (stub) VALUES ('a');  
SELECT LAST_INSERT_ID();  
複製代碼

此方案較爲簡單,但缺點較爲明顯:存在單點問題,強依賴DB,當DB異常時,整個系統不可用。配置主從能夠增長可用性。另外性能瓶頸限制在單臺Mysql的讀寫性能。
另有一種主鍵生成策略,相似sequence表方案,更好的解決了單點和性能瓶頸問題。這一方案的總體思想是:創建2個以上的全局ID生成的服務器,每一個服務器上只部署一個數據庫,每一個庫有一張sequence表用於記錄當前全局ID。 表中增加的步長是庫的數量,起始值依次錯開,這樣就能將ID的生成散列到各個數據庫上

這種方案將生成ID的壓力均勻分佈在兩臺機器上,同時提供了系統容錯,第一臺出現了錯誤,能夠自動切換到第二臺獲取ID。但有幾個缺點:系統添加機器,水平擴展較複雜;每次獲取ID都要讀取一次DB,DB的壓力仍是很大,只能經過堆機器來提高性能。

Snowflake分佈式自增ID算法

Twitter的snowfalke算法解決了分佈式系統生成全局ID的需求,生成64位Long型數字,組成部分:

  • 第一位未使用
  • 接下來的41位是毫秒級時間,41位的長度能夠表示69年的時間
  • 5位datacenterId,5位workerId。10位長度最多支持部署1024個節點
  • 最後12位是毫秒內計數,12位的計數順序號支持每一個節點每毫秒產生4096個ID序列。

數據遷移、擴容問題

當業務高速發展、面臨性能和存儲瓶頸時,纔會考慮分片設計,此時就不可避免的須要考慮歷史數據的遷移問題。通常作法是先讀出歷史數據,而後按照指定的分片規則再將數據寫入到各分片節點中。此外還須要根據當前的數據量個QPS,以及業務發展速度,進行容量規劃,推算出大概須要多少分片(通常建議單個分片的單表數據量不超過1000W)

何時考慮分庫分表

能不分就不分

並非全部表都須要切分,主要仍是看數據的增加速度。切分後在某種程度上提高了業務的複雜程度。不到萬不得已不要輕易使用分庫分表這個「大招」,避免「過分設計」和「過早優化」。分庫分表以前,先盡力作力所能及的優化:升級硬件、升級網絡、讀寫分離、索引優化等。當數據量達到單表瓶頸後,在考慮分庫分表。

數據量過大,正常運維影響業務訪問

這裏的運維是指:

  • 對數據庫備份,若是單表太大,備份時須要大量的磁盤IO和網絡IO
  • 對一個很大的表作DDL,MYSQL會鎖住整個表,這個時間會很長,這段時間業務不能訪問此表,影響很大。
  • 大表常常訪問和更新,就更有可能出現鎖等待。

隨着業務發展,須要對某些字段垂直拆分

這裏就不舉例了。在實際業務中均可能會碰到,有些不常常訪問或者更新頻率低的字段應該從大表中分離出去。

數據量快速增加

隨着業務的快速發展,單表中的數據量會持續增加,當性能接近瓶頸時,就須要考慮水平切分,作分庫分表了。


參考連接:
www.cnblogs.com/butterfly10…
www.cnblogs.com/littlechara…

相關文章
相關標籤/搜索