無論是IO瓶頸仍是CPU瓶頸,最終都會致使數據庫的活躍鏈接數增長,進而逼近甚至達到數據庫可承載的活躍鏈接數的閾值。在業務service來看, 就是可用數據庫鏈接少甚至無鏈接可用,接下來就能夠想象了(併發量、吞吐量、崩潰)。html
分庫分表能有效緩解單機和單錶帶來的性能瓶頸和壓力,突破網絡IO、硬件資源、鏈接數的瓶頸,同時也帶來一些問題,下面將描述這些問題和解決思路。算法
當更新內容同時存在於不一樣庫找那個,不可避免會帶來跨庫事務問題。跨分片事務也是分佈式事務,沒有簡單的方案,通常可以使用「XA協議」和「兩階段提交」處理。
分佈式事務能最大限度保證了數據庫操做的原子性。但在提交事務時須要協調多個節點,推後了提交事務的時間點,延長了事務的執行時間,致使事務在訪問共享資源時發生衝突或死鎖的機率增高。隨着數據庫節點的增多,這種趨勢會愈來愈嚴重,從而成爲系統在數據庫層面上水平擴展的枷鎖。sql
對於那些性能要求很高,但對一致性要求不高的系統,每每不苛求系統的實時一致性,只要在容許的時間段內達到最終一致性便可,可採用事務補償的方式。與事務在執行中發生錯誤馬上回滾的方式不一樣,事務補償是一種過後檢查補救的措施,一些常見的實現方法有:對數據進行對帳檢查,基於日誌進行對比,按期同標準數據來源進行同步等。數據庫
切分以前,系統中不少列表和詳情表的數據能夠經過join來完成,可是切分以後,數據可能分佈在不一樣的節點上,此時join帶來的問題就比較麻煩了,考慮到性能,儘可能避免使用Join查詢。解決的一些方法:緩存
全局表,也可看作「數據字典表」,就是系統中全部模塊均可能依賴的一些表,爲了不庫join查詢,能夠將這類表在每一個數據庫中都保存一份。這些數據一般不多修改,因此沒必要擔憂一致性的問題。bash
一種典型的反範式設計,利用空間換時間,爲了性能而避免join查詢。例如,訂單表在保存userId的時候,也將userName也冗餘的保存一份,這樣查詢訂單詳情順表就能夠查到用戶名userName,就不用查詢買家user表了。但這種方法適用場景也有限,比較適用依賴字段比較少的狀況,而冗餘字段的一致性也較難保證。服務器
在系統service業務層面,分兩次查詢,第一次查詢的結果集找出關聯的數據id,而後根據id發起器二次請求獲得關聯數據,最後將得到的結果進行字段組裝。這是比較經常使用的方法。網絡
關係型數據庫中,若是已經肯定了表之間的關聯關係(如訂單表和訂單詳情表),而且將那些存在關聯關係的表記錄存放在同一個分片上,那麼就能較好地避免跨分片join的問題,能夠在一個分片內進行join。在1:1或1:n的狀況下,一般按照主表的ID進行主鍵切分。併發
跨節點多庫進行查詢時,會出現limit分頁、order by 排序等問題。分頁須要按照指定字段進行排序,當排序字段就是分頁字段時,經過分片規則就比較容易定位到指定的分片;當排序字段非分片字段時,就變得比較複雜.須要先在不一樣的分片節點中將數據進行排序並返回,而後將不一樣分片返回的結果集進行彙總和再次排序,最終返回給用戶以下圖:運維
上圖只是取第一頁的數據,對性能影響還不是很大。可是若是取得頁數很大,狀況就變得複雜的多,由於各分片節點中的數據多是隨機的,爲了排序的準確性,須要將全部節點的前N頁數據都排序好作合併,最後再進行總體排序,這樣的操做很耗費CPU和內存資源,因此頁數越大,系統性能就會越差。在分庫分表環境中,因爲表中數據同時存在不一樣數據庫中,主鍵值平時使用的自增加將無用武之地,某個分區數據庫自生成ID沒法保證全局惟一。所以須要單獨設計全局主鍵,避免跨庫主鍵重複問題。這裏有一些策略:
UUID標準形式是32個16進制數字,分爲5段,形式是8-4-4-4-12的36個字符。 UUID是最簡單的方案,本地生成,性能高,沒有網絡耗時,可是缺點明顯,佔用存儲空間多,另外做爲主鍵創建索引和基於索引進行查詢都存在性能問題,尤爲是InnoDb引擎下,UUID的無序性會致使索引位置頻繁變更,致使分頁。
在數據庫中創建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的生成散列到各個數據庫上
當業務高速發展、面臨性能和存儲瓶頸時,纔會考慮分片設計,此時就不可避免的須要考慮歷史數據的遷移問題。通常作法是先讀出歷史數據,而後按照指定的分片規則再將數據寫入到各分片節點中。此外還須要根據當前的數據量個QPS,以及業務發展速度,進行容量規劃,推算出大概須要多少分片(通常建議單個分片的單表數據量不超過1000W)
並非全部表都須要切分,主要仍是看數據的增加速度。切分後在某種程度上提高了業務的複雜程度。不到萬不得已不要輕易使用分庫分表這個「大招」,避免「過分設計」和「過早優化」。分庫分表以前,先盡力作力所能及的優化:升級硬件、升級網絡、讀寫分離、索引優化等。當數據量達到單表瓶頸後,在考慮分庫分表。
這裏的運維是指:
這裏就不舉例了。在實際業務中均可能會碰到,有些不常常訪問或者更新頻率低的字段應該從大表中分離出去。
隨着業務的快速發展,單表中的數據量會持續增加,當性能接近瓶頸時,就須要考慮水平切分,作分庫分表了。
參考連接:
www.cnblogs.com/butterfly10…
www.cnblogs.com/littlechara…