小明是一家初創電商平臺的開發人員,他負責賣家模塊的功能開發,其中涉及了店鋪、商品的相關業務,設計以下數據庫 :
經過如下SQL可以獲取到商品相關的店鋪信息、地理區域信息 :java
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:
把數據分散在不一樣的數據庫中,使得單一數據庫的數據量變小來緩解單一數據庫的性能問題,從而達到提高數據庫 性能的目的,以下圖:將電商數據庫拆分爲若干獨立的數據庫,而且對於大表也拆分爲若干小表,經過這種數據庫 拆分的方法來解決數據庫的性能問題。
分庫分表就是爲了解決因爲數據量過大而致使數據庫性能下降的問題,將原來獨立的數據庫拆分紅若干數據庫組成,將數據大表分紅若干數據表組成,使得單一數據庫、單一數據表的數據量變小,從而達到提高數據庫性能的目的。sql
分庫分表包括分庫和分表兩個部分,在生產中一般包括 :垂直分庫、水平分庫、垂直分表、水平分表四種方式。數據庫
下邊經過一個商品查詢的案例來垂直分表 :
一般在商品列表中是否是顯示商品詳情信息的,以下圖 :
用戶在瀏覽商品列表時,只有對某商品感興趣時纔會查看商品的詳細描述。所以,商品信息中商品描述字段訪問頻次較低,且該字段存儲佔用空間較大,訪問單個數據IO時間較長;商品信息中商品名稱、商品圖片、商品價格等其餘字段數據訪問頻次較高。
因爲這兩種數據的特性不同,所以他考慮將商品信息表拆分以下 :
將訪問頻次低的商品描述信息單獨存放在一張表中,訪問頻次較高的商品基本信息單獨放在一張表中。
商品列表可採用如下sql :apache
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爭搶。垂直切分帶來的性能提高主要集中在熱門數據的操做效率上,並且磁盤爭用狀況減小。
一般咱們按如下原則進行垂直拆分 :
一、把不經常使用的字段單獨放在一張表;
二、把text,blob等大字段拆分出來放在附表中;
三、常常組合查詢的列放在一張表中;網絡
經過垂直分表能獲得來必定程度的提高,可是尚未達到要求,而且磁盤空間也快不夠來,由於數據仍是始終限制在一臺服務器,庫內垂直分表只解決來單一表數據量過大的問題,但沒有將表分佈到不一樣的服務器上,所以每一個表仍是競爭同一個物理機的CPU、內存、網絡IO
、磁盤。
通過思考,他把原來的SELLER_DB(賣家庫),分爲來PRODUCT_DB (商品庫)和STORE_DB(店鋪庫),並把這兩個庫分散到不一樣服務器,以下圖 :
因爲商品信息與商品描述業務耦合度較高,所以一塊兒被存放在PRODUCT_DB(商品庫);而店鋪信息相對獨立,所以單獨被存放在STORE_DB(店鋪庫)。
小明進行的這一步優化,就叫垂直分庫。
垂直分庫是指按照業務將表進行分類,分佈到不一樣的數據庫上面,每一個庫能夠放不一樣的服務器上,它的核心理念是專庫專用。
它帶來的提高是 :架構
通過垂直分庫後,數據庫性能問題獲得必定程度的解決,可是隨着業務量的增加,PRODUCT_DB(商品庫)單庫存儲數據已經超出預估。粗糧統計,目前有8W店鋪,每一個店鋪平均150個不一樣規格的商品,再算增加,那商品數量的往1500w+上預估,而且PRODUCT_DB(商品庫)屬於訪問很是頻繁的資源,單臺服務器已經沒法支撐。此時該如何優化?
再次分庫?可是從業務角度分析,目前狀況已經沒法再次垂直分庫。能夠嘗試水平分庫,將店鋪ID爲單數的和店鋪ID爲雙數的商品信息分別放在兩個庫中。
也就是說,要操做其某條數據,先分析這條數據所屬的店鋪ID。若是店鋪ID爲雙數,將此操做映射至PRODUCT_DB1(商品庫1);若是ID爲單數,將操做映射至RRODUCT_DB2(商品庫2)。此操做要訪問數據庫名稱的表達式爲RRODUCT_DB【店鋪ID%2 + 1】.
小明進行的這一步優化,就叫水平分庫。
水平分庫是把同一個表的數據按必定規則拆分到不一樣的數據庫中,每一個庫能夠放不一樣的服務器上。
對比 :垂直分庫是把不一樣表拆到不一樣數據庫中,它是對數據行的拆分,不影響表結構。
它帶來的提高是 :併發
按照水平分庫的思路對他把PRODUCT_DB_X(商品庫)內的表也能夠進行水平拆分,其目的也是爲解決單表數據量大的問題,以下圖 :
與水平分庫的思路相似,不過此次操做的目標是表,商品信息及商品描述被分紅了兩套表。若是商品ID爲雙數,將此操做映射至商品信息1表;若是商品ID爲單數,將操做映射至商品信息2表。此操做要訪問表名稱的表達式爲商品信息【商品ID%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...
但垂直分庫後【商品信息】和【店鋪信息】不在一個數據庫,甚至不在一臺服務器,沒法進行關聯查詢。
可將原關聯查詢分爲兩次查詢,第一次查詢的結果集中找出關聯數據id,而後根據id發起第二次請求獲得關聯數據,最後將得到到的數據進行拼裝。
跨節點多庫進行查詢時,limit分頁、order by排序等問題,就變得比較複雜了。須要先在不一樣的分片節點中將數據進行排序並返回,而後將不一樣分片返回的結果集進行彙總和再次排序。
如,進行水平分庫後的商品庫,按ID倒序排序分頁,取第一頁 :
以上流程是取第一頁的數據,性能影響不大,但因爲商品信息的分佈在各數據庫的數據多是隨機的,若是是取第N頁,須要將全部節點前N頁數據都取出來合併,再進行總體的排序,操做效率可想而知。因此請求頁數越大,系統的性能也會越差。在使用Max、Min、Sum、Count之類的函數進行計算的時候,與排序分頁同理,也須要先在每一個分片上執行相應的函數,而後將各個分片的結果集進行彙總和再次計算,最終將結果返回。
在分庫分表環境中,因爲表中數據同時存在不一樣數據庫中,主鍵值平時使用的自增加將無用武之地,某個分區數據庫生成的ID沒法保證全局惟一。所以須要單獨設計全局主鍵,比避免跨庫主鍵重複問題。
實際的應用場景中,參數表、數據字典表等都是數據量較小,變更少,並且屬於高頻聯合查詢的依賴表。例子中地理區域表也屬於此類型。
能夠將這類表在每一個數據庫都保存一份,全部對公共表的更新操做都同時發送到分庫執行。
因爲分庫分表以後,數據被分散在不一樣的數據庫、服務器。所以,對數據的操做也就沒法經過常規方式完成,而且它還帶來了一系列的問題。好在,這些問題不是全部都須要咱們在應用層面上解決,其中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訪問已經分庫 分表、讀寫分離的多個數據源,而不用關心數據源的數量以及數據如何分佈。