分庫分表之第一篇
- 1.概述
- 1.1.分庫分表是什么
- 1.2.分庫分表的方式
- 1.2.1.垂直分表
- 1.2.2.垂直分庫
- 1.2.3.水平分庫
- 1.2.4.水平分表
- 1.2.5 小結
- 1.3.分庫分表帶來的問題
- 1.3.1.事務一致性問題
- 1.3.2.跨節點關聯查詢
- 1.3.3.跨節點分頁、排序函式
- 1.3.4.主鍵避重
- 1.3.5.公共表
- 1.4 Sharding-JDBC介紹
- 1.4.1 Sharding-JDBC介紹
- 1.4.2 與jdbc性能對比
1.概述
1.1.分庫分表是什么
小明是一家初創電商平臺的開發人員,他負責賣家模塊的功能開發,其中涉及了店鋪、商品的相關業務,設計如下資料庫 :
通過以下SQL能夠獲取到商品相關的店鋪資訊、地理區域資訊 :
SELECT p.*,r.[地理區域名稱],s.[店鋪名稱],s.[信譽] FROM [商品資訊] p
LEFT JOIN [地理區域] r ON p.[產地] = r.[地理區域編碼] LEFT JOIN [店鋪資訊] s ON p.id = s.[所屬店鋪]
WHERE p.id = ?
形成類似以下串列展示 :
隨著公司業務快速發展,資料庫中的資料量猛增,訪問性能也變慢了,優化迫在眉睫,分析一下問題出現在哪兒 呢? 關系型資料庫本身比較容易成為系統瓶頸,單機存盤容量、連接數、處理能力都有限,當單表的資料量達到 1000W或100G以后,由于查詢維度較多,即使添加從庫、優化索引,做很多操作時性能仍下降嚴重,
方案1:
通過提升服務器硬體能力來提高資料處理能力,比如增加存盤容量 、CPU等,這種方案成本很高,并且如果瓶頸在
MySQL本身那么提高硬體也是有很的,
方案2:
把資料分散在不同的資料庫中,使得單一資料庫的資料量變小來緩解單一資料庫的性能問題,從而達到提升資料庫 性能的目的,如下圖:將電商資料庫拆分為若干獨立的資料庫,并且對于大表也拆分為若干小表,通過這種資料庫 拆分的方法來解決資料庫的性能問題,
分庫分表就是為了解決由于資料量過大而導致資料庫性能降低的問題,將原來獨立的資料庫拆分成若干資料庫組成,將資料大表分成若干資料表組成,使得單一資料庫、單一資料表的資料量變小,從而達到提升資料庫性能的目的,
1.2.分庫分表的方式
分庫分表包括分庫和分表兩個部分,在生產中通常包括 :垂直分庫、水平分庫、垂直分表、水平分表四種方式,
1.2.1.垂直分表
下邊通過一個商品查詢的案例來垂直分表 :
通常在商品串列中是不是顯示商品詳情資訊的,如下圖 :
用戶在瀏覽商品串列時,只有對某商品感興趣時才會查看商品的詳細描述,因此,商品資訊中商品描述欄位訪問頻次較低,且該欄位存盤占用空間較大,訪問單個資料IO時間較長;商品資訊中商品名稱、商品圖片、商品價格等其他欄位資料訪問頻次較高,
由于這兩種資料的特性不一樣,因此他考慮將商品資訊表拆分如下 :
將訪問頻次低的商品描述資訊單獨存放在一張表中,訪問頻次較高的商品基本資訊單獨放在一張表中,
商品串列可采用以下sql :
SELECT p.*,r.[地理區域名稱],s.[店鋪名稱],s.[信譽] FROM [商品資訊] p
LEFT JOIN [地理區域] r ON p.[產地] = r.[地理區域編碼] LEFT JOIN [店鋪資訊] s ON p.id = s.[所屬店鋪] WHERE...ORDER BY...LIMIT...
需要獲取商品描述時,再通過以下sql獲取 :
SELECT *
FROM [商品描述] WHERE [商品ID] = ?
小明進行的這一步優化,就叫垂直分表,
垂直分表定義 :將一個表按照欄位分成多表,每個表存盤其中一部分欄位,
它帶來的提升是 :
1.為了避免IO爭搶并減少鎖表的幾率,查看詳情的用戶與商品資訊瀏覽互不影響,
2.充分發揮熱門資料的操作效率,商品資訊的操作的高效率不會被商品描述的低效率所拖累,
注意 :
為什么大欄位IO效率低 :
第一是由于資料量本身大,需要更長的讀取時間;
第二是跨頁,頁是資料庫存盤單位,很多查找及定位操作都是以頁為單位,單頁內的資料行越多資料庫整體性能越好,而大欄位占用空間大,單頁記憶體儲行數少,因此IO效率較低,
第三,資料庫以行為單位將資料加載到記憶體中,這樣表中欄位長度較短且訪問頻率較高,記憶體能加載更多的資料,命中率更高,減少來磁盤IO,從而提升來資料庫性能,
一般來說,某業務物體中的各個資料項的訪問頻次是不一樣的,部分資料項可能是占用存盤空間比較大的BLOB或是TEXT,例如上例中的商品描述,所以,當表資料量很大時,可以將表按欄位切開,將熱門欄位、冷門欄位分開放置在不同庫中,這些庫可以放在不同的存盤設定上,避免IO爭搶,垂直切分帶來的性能提升主要集中在熱門資料的操作效率上,而且磁盤爭用情況減少,
通常我們按以下原則進行垂直拆分 :
1、把不常用的欄位單獨放在一張表;
2、把text,blob等大欄位拆分出來放在附表中;
3、經常組合查詢的列放在一張表中;
1.2.2.垂直分庫
通過垂直分表能得到來一定程度的提升,但是還沒有達到要求,并且磁盤空間也快不夠來,因為資料還是始終限制在一臺服務器,庫內垂直分表只解決來單一表資料量過大的問題,但沒有將表分布到不同的服務器上,因此每個表還是競爭同一個物理機的CPU、記憶體、網路IO
、磁盤,
經過思考,他把原來的SELLER_DB(賣家庫),分為來PRODUCT_DB (商品庫)和STORE_DB(店鋪庫),并把這兩個庫分散到不同服務器,如下圖 :
由于商品資訊與商品描述業務耦合度較高,因此一起被存放在PRODUCT_DB(商品庫);而店鋪資訊相對獨立,因此單獨被存放在STORE_DB(店鋪庫),
小明進行的這一步優化,就叫垂直分庫,
垂直分庫是指按照業務將表進行分類,分布到不同的資料庫上面,每個庫可以放不同的服務器上,它的核心理念是專庫專用,
它帶來的提升是 :
- 解決業務層面的耦合,業務清晰
- 能對不同業務的資料進行分級管理、維護、監控、擴展等
- 高并發場景下,垂直分庫一定程度的提升IO、資料庫連接數、降低單機硬體資源的瓶頸
垂直分庫通過將表按業務分類,然后分庫在不同資料庫,并且可以將這些資料庫部署在不同服務器上,從而達到多個服務器共同分攤壓力的效果,但是依然沒有解決單表資料量過大的問題,
1.2.3.水平分庫
經過垂直分庫后,資料庫性能問題得到一定程度的解決,但是隨著業務量的增長,PRODUCT_DB(商品庫)單庫存盤資料已經超出預估,粗糧統計,目前有8W店鋪,每個店鋪平均150個不同規格的商品,再算增長,那商品數量的往1500w+上預估,并且PRODUCT_DB(商品庫)屬于訪問非常頻繁的資源,單臺服務器已經無法支撐,此時該如何優化?
再次分庫?但是從業務角度分析,目前情況已經無法再次垂直分庫,可以嘗試水平分庫,將店鋪ID為單數的和店鋪ID為雙數的商品資訊分別放在兩個庫中,
也就是說,要操作其某條資料,先分析這條資料所屬的店鋪ID,如果店鋪ID為雙數,將此操作映射至PRODUCT_DB1(商品庫1);如果ID為單數,將操作映射至RRODUCT_DB2(商品庫2),此操作要訪問資料庫名稱的運算式為RRODUCT_DB【店鋪ID%2 + 1】.
小明進行的這一步優化,就叫水平分庫,
水平分庫是把同一個表的資料按一定規則拆分到不同的資料庫中,每個庫可以放不同的服務器上,
對比 :垂直分庫是把不同表拆到不同資料庫中,它是對資料行的拆分,不影響表結構,
它帶來的提升是 :
- 解決來單庫大資料,高并發的性能瓶頸,
- 提高來系統的穩定性及可用性,
穩定性體現在IO沖突減少,鎖定減少,可用性指某個庫出問題,部分可用,
當一個應用難以再細粒度的垂直切分,或切分后資料量行巨大,存在單庫讀寫、存盤性能瓶頸,這時候就需要進行水平分庫了,經過水平切分的優化,往往能解決單庫存盤量及性能瓶頸,但由于同一個表被分配在不同的資料庫,需要額外進行資料操作的路由作業,因此大大提升了系統復雜度,
1.2.4.水平分表
按照水平分庫的思路對他把PRODUCT_DB_X(商品庫)內的表也可以進行水平拆分,其目的也是為解決單表資料量大的問題,如下圖 :
與水平分庫的思路類似,不過這次操作的目標是表,商品資訊及商品描述被分成了兩套表,如果商品ID為雙數,將此操作映射至商品資訊1表;如果商品ID為單數,將操作映射至商品資訊2表,此操作要訪問表名稱的運算式為商品資訊【商品ID%2 + 1】,
小明進行的這一步優化,就叫水平分表,
水平分表是在同一個資料庫內,把同一個表的資料按一定規則拆到多個表中,
它帶來的提升是 :
- 優化單一表資料量過大而產生的性能問題
- 避免IO爭搶并減少鎖表的幾率
庫內的水平分表,解決來單一表資料量過大的問題,分出來的小表中只包含一部分資料,從而使得單個表的資料量變小,提高檢索性能,
1.2.5 小結
介紹來分庫分表的幾種方式,它們分別是垂直分表、垂直分庫、水平分庫和水平分表 :
垂直分表 :可以把一個寬表的欄位按訪問頻次,是否是大欄位的原則拆分為多個表,這樣既能使業務清晰,還能提升部分性能,拆分后,盡量從業務角度避免聯查,否則性能方面將得不償失,
垂直分庫 :可以把多個表按業務耦合松緊歸類,分別存放在不同的庫,這些庫可以分布在不同服務器,從而使訪問壓力被能服務器負載,大大提升性能,同時能提高整體架構的業務清晰度,不同的業務庫可根據自身情況定制優化方案,但是它需要解決跨庫帶來的所有復雜問題,
水平分庫 :可以把一個表的資料(按資料行)分到多個不同的庫,每個庫只有這個表的部分資料,這些庫可以分布在不同服務器,從而使訪問壓力被多服務器負載,大大提升性能,它不僅需要解決跨庫帶來的所有復雜問題,還要解決資料路由的問題(資料路由問題后邊介紹),
水平分表 :可以把一個表的資料(按資料行)分到多個同一個資料庫的多張表中,每個表只有這個表的部分資料,這樣做能小幅提升性能,它僅僅作為水平分庫的一個補充優化,
一般來說,在系統設計階段就應該根據業務耦合松緊來確定垂直分庫,垂直分表方案,在資料量及訪問壓力不是特別大的情況,首先考慮緩沖、讀寫分離、索引技術等方案,若資料量極大,且持續增長,再考慮水平分庫水平分表方案,
1.3.分庫分表帶來的問題
分庫分表能有效的緩解來單機和單庫帶來的性能瓶頸和壓力,突破網路IO、硬體資源、連接數的瓶頸,同時也帶來了一些問題,
1.3.1.事務一致性問題
由于分庫分表把資料分布在不同庫甚至不同服務器,不可避免會帶來分布式事務問題,
1.3.2.跨節點關聯查詢
在沒有分庫前,我們檢索商品時可以通過以下SQL對店鋪資訊進行關聯查詢 :
SELECT p.*,r.[地理區域名稱],s.[店鋪名稱],s.[信譽] FROM [商品資訊] p
LEFT JOIN [地理區域] r ON p.[產地] = r.[地理區域編碼] LEFT JOIN [店鋪資訊] s ON p.id = s.[所屬店鋪] WHERE...ORDER BY...LIMIT...
但垂直分庫后【商品資訊】和【店鋪資訊】不在一個資料庫,甚至不在一臺服務器,無法進行關聯查詢,
可將原關聯查詢分為兩次查詢,第一次查詢的結果集中找出關聯資料id,然后根據id發起第二次請求得到關聯資料,最后將獲得到的資料進行拼裝,
1.3.3.跨節點分頁、排序函式
跨節點多庫進行查詢時,limit分頁、order by排序等問題,就變得比較復雜了,需要先在不同的分片節點中將資料進行排序并回傳,然后將不同分片回傳的結果集進行匯總和再次排序,
如,進行水平分庫后的商品庫,按ID倒序排序分頁,取第一頁 :
以上流程是取第一頁的資料,性能影響不大,但由于商品資訊的分布在各資料庫的資料可能是隨機的,如果是取第N頁,需要將所有節點前N頁資料都取出來合并,再進行整體的排序,操作效率可想而知,所以請求頁數越大,系統的性能也會越差,在使用Max、Min、Sum、Count之類的函式進行計算的時候,與排序分頁同理,也需要先在每個分片上執行相應的函式,然后將各個分片的結果集進行匯總和再次計算,最終將結果回傳,
1.3.4.主鍵避重
在分庫分表環境中,由于表中資料同時存在不同資料庫中,主鍵值平時使用的自增長將無用武之地,某個磁區資料庫生成的ID無法保證全域唯一,因此需要單獨設計全域主鍵,比避免跨庫主鍵重復問題,
1.3.5.公共表
實際的應用場景中,引數表、資料字典表等都是資料量較小,變動少,而且屬于高頻聯合查詢的依賴表,例子中地理區域表也屬于此型別,
可以將這類表在每個資料庫都保存一份,所有對公共表的更新操作都同時發送到分庫執行,
由于分庫分表之后,資料被分散在不同的資料庫、服務器,因此,對資料的操作也就無法通過常規方式完成,并且它還帶來了一系列的問題,好在,這些問題不是所有都需要我們在應用層面上解決,其中Sharding-JDBC中間件可供選擇,
1.4 Sharding-JDBC介紹
1.4.1 Sharding-JDBC介紹
Sharding-JDBC是當當網研發的開源分布式資料庫中間件,從 3.0 開始Sharding-JDBC被包含在 Sharding-Sphere 中,之后該專案進入進入Apache范訓器,4.0版本之后的版本為Apache版本,
ShardingSphere是一套開源的分布式資料庫中間件解決方案組成的生態圈,它由Sharding-JDBC、Sharding- Proxy和Sharding-Sidecar(計劃中)這3款相互獨立的產品組成, 他們均提供標準化的資料分片、分布式事務和 資料庫治理功能,可適用于如Java同構、異構語言、容器、云原生等各種多樣化的應用場景,
官方地址:https://shardingsphere.apache.org/document/current/cn/overview/
咱們目前只需關注Sharding-JDBC,它定位為輕量級Java框架,在Java的JDBC層提供的額外服務, 它使用客戶端 直連資料庫,以jar包形式提供服務,無需額外部署和依賴,可理解為增強版的JDBC驅動,完全兼容JDBC和各種 ORM框架,
Sharding-JDBC的核心功能為資料分片和讀寫分離,通過Sharding-JDBC,應用可以透明的使用jdbc訪問已經分庫 分表、讀寫分離的多個資料源,而不用關心資料源的數量以及資料如何分布,
- 適用于任何基于Java的ORM框架,如: Hibernate, Mybatis, Spring JDBC Template或直接使用JDBC,
- 基于任何第三方的資料庫連接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP等,
- 支持任意實作JDBC規范的資料庫,目前支持MySQL,Oracle,SQLServer和PostgreSQL,

上圖展示了Sharding-Jdbc的作業方式,使用Sharding-Jdbc前需要人工對資料庫進行分庫分表,在應用程式中加入 Sharding-Jdbc的Jar包,應用程式通過Sharding-Jdbc操作分庫分表后的資料庫和資料表,由于Sharding-Jdbc是對 Jdbc驅動的增強,使用Sharding-Jdbc就像使用Jdbc驅動一樣,在應用程式中是無需指定具體要操作的分庫和分表 的,
1.4.2 與jdbc性能對比
- 性能損耗測驗 :服務器資源充足、并發數相同,比較JDBC和Sharding-JDBC性能損耗,Sharding-JDBC相對JDBC損耗不超過7%,
基準測驗性能對比
- 性能對比測驗:服務器資源使用到極限,相同的場景JDBC與Sharding-JDBC的吞吐量相當,
- 性能對比測驗:服務器資源使用到極限,Sharding-JDBC采用分庫分表后,Sharding-JDBC吞吐量較JDBC不分表有接近2倍的提升,

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/29207.html
標籤:架構設計
上一篇:Springboot 專案原始碼 Activiti6 作業流 vue.js html 跨域 前后分離 websocket即時通訊
下一篇:Docker swarm實戰總結
