一文快速入門分庫分表(必修課)

之前有不少剛入坑 Java 的粉絲留言,想系統的學習一下分庫分表相關技術,可我一直沒下定決心搞,眼下趕上公司項目在使用 sharing-jdbc  對現有 MySQL 架構做分庫分表的改造,所以藉此機會出一系分庫分表落地實踐的文章,也算是自己對架構學習的一個總結。

我在網上陸陸續續的也看了一些有關於分庫分表的文章,可發現網上同質化的資料有點多,而且知識點又都比較零碎,還沒有詳細的實戰案例。爲了更深入的學習下,我在某些平臺買了點付費課程,看了幾節課發現有點經驗的人看還可以,但對於新手入門來說,其實學習難度還是蠻大的。

爲了讓新手也能看得懂,有些知識點我可能會用更多的篇幅加以描述,希望大家不要嫌我囉嗦,等這分庫分表系列文章完結後,我會把它做成 PDF 文檔開源出去,能幫一個算一個吧!如果發現文中有哪些錯誤或不嚴謹之處,歡迎大家交流指正。

具體實踐分庫分表之前在囉嗦幾句,回頭複習下分庫分表的基礎概念。

什麼是分庫分表

其實 分庫分表 是兩個概念,只不過通常分庫與分表的操作會同時進行,以至於我們習慣性的將它們合在一起叫做分庫分表。

分庫分表是爲了解決由於庫、表數據量過大,而導致數據庫性能持續下降的問題。按照一定的規則,將原本數據量大的數據庫拆分成多個單獨的數據庫,將原本數據量大的表拆分成若干個數據表,使得單一的庫、表性能達到最優的效果(響應速度快),以此提升整體數據庫性能。

如何分庫分表

分庫分表的核心理念就是對數據進行切分(Sharding),以及切分後如何對數據的快速定位與查詢結果整合。而分庫與分表都可以從:垂直(縱向)和 水平(橫向)兩種緯度進行切分。

分庫分表


下邊我們就以訂單相關的業務舉例,看看如何做庫、表的 垂直水平 切分。

垂直切分

垂直切分有 垂直 分庫 和 垂直分表。

1、垂直分庫

垂直分庫相對來說是比較好理解的,核心理念就四個字:專庫專用

按業務類型對錶進行分類,像訂單、支付、優惠券、積分等相應的表放在對應的數據庫中。開發者不可以跨庫直連別的業務數據庫,想要其他業務數據,對應業務方可以提供 API 接口,這就是微服務的初始形態。

垂直分庫很大程度上取決於業務的劃分,但有時候業務間的劃分並不是那麼清晰,比如:訂單數據的拆分要考慮到與其他業務間的關聯關係,並不是說直接把訂單相關的表放在一個庫裏這麼簡單。

在一定程度上,垂直分庫似乎提升了一些數據庫性能,可實際上並沒有解決由於單表數據量過大導致的性能問題,所以就需要配合水平切分方式來解決。

垂直分庫

2、垂直分表

垂直分表是基於數據表的列(字段)爲依據切分的,是一種大表拆小表的模式。

例如:一張 order 訂單表,將訂單金額、訂單編號等訪問頻繁的字段,單獨拆成一張表,把 blob 類型這樣的大字段或訪問不頻繁的字段,拆分出來創建一個單獨的擴展表 work_extend ,這樣每張表只存儲原表的一部分字段,再將拆分出來的表分散到不同的庫中。

垂直分表

我們知道數據庫是以行爲單位將數據加載到內存中,這樣拆分以後核心表大多是訪問頻率較高的字段,而且字段長度也都較短,因而可以加載更多數據到內存中,來增加查詢的命中率,減少磁盤 IO,以此來提升數據庫性能。

垂直切分的優點:

垂直切分的缺點:


水平切分

前邊說了垂直切分還是會存在單庫、表數據量過大的問題,當我們的應用已經無法在細粒度的垂直切分時, 依舊存在單庫讀寫、存儲性能瓶頸,這時就要配合水平切分一起了,水平切分能大幅提升數據庫性能。

1、水平分庫

水平分庫是把同一個表按一定規則拆分到不同的數據庫中,每個庫可以位於不同的服務器上,以此實現水平擴展,是一種常見的提升數據庫性能的方式。

這種方案往往能解決單庫存儲量及性能瓶頸問題,但由於同一個表被分配在不同的數據庫中,數據的訪問需要額外的路由工作,因此係統的複雜度也被提升了。

例如下圖,訂單DB_1訂單DB_1訂單DB_3 三個數據庫內有完全相同的表 order,我們在訪問某一筆訂單時可以通過對訂單的訂單編號取模的方式 訂單編號 mod 3 (數據庫實例數) ,指定該訂單應該在哪個數據庫中操作。

水平分庫

2、水平分表

水平分表是在同一個數據庫內,把一張大數據量的表按一定規則,切分成多個結構完全相同表,而每個表只存原表的一部分數據。

例如:一張 order 訂單表有 900 萬數據,經過水平拆分出來三個表,order_1order_2order_3,每張表存有數據 300 萬,以此類推。

水平分表

水平分表儘管拆分了表,但子表都還是在同一個數據庫實例中,只是解決了單一表數據量過大的問題,並沒有將拆分後的表分散到不同的機器上,還在競爭同一個物理機的 CPU、內存、網絡 IO 等。要想進一步提升性能,就需要將拆分後的表分散到不同的數據庫中,達到分佈式的效果。

分庫分表

水平切分的優點:

水平切分的缺點:


一定規則是什麼

我們上邊提到過很多次 一定規則 ,這個規則其實是一種路由算法,就是決定一條數據具體應該存在哪個數據庫的哪張表裏。

常見的有 取模算法範圍限定算法

1、取模算法

按字段取模(對 hash 結果取餘數 (hash() mod N),N 爲數據庫實例數或子表數量)是最爲常見的一種切分方式。

還拿 order 訂單表舉例,先對數據庫從 0 到 N-1 進行編號,對 order 訂單表中 work_no 訂單編號字段進行取模,得到餘數 ii=0存第一個庫,i=1存第二個庫,i=2存第三個庫.... 以此類推。

這樣同一筆訂單的數據都會存在同一個庫、表裏,查詢時用相同的規則,用 work_no 訂單編號作爲查詢條件,就能快速的定位到數據。

優點:

缺點:

2、範圍限定算法

按照 時間區間ID區間 來切分,比如:我們切分的是用戶表,可以定義每個庫的 User 表裏只存 10000 條數據,第一個庫只存 userId 從 1 ~ 9999 的數據,第二個庫存 userId 爲 10000 ~ 20000,第三個庫存 userId 爲 20001~ 30000...... 以此類推,按時間範圍也是同理。

優點:

缺點:

分庫分表的難點

1、分佈式事務

由於表分佈在不同庫中,不可避免會帶來跨庫事務問題。一般可使用 "三階段提交 "和"兩階段提交" 處理,但是這種方式性能較差,代碼開發量也比較大。通常做法是做到最終一致性的方案,如果不苛求系統的實時一致性,只要在允許的時間段內達到最終一致性即可,採用事務補償的方式。

這裏我應用阿里的分佈式事務框架Seata 來做分佈式事務的管理,後邊會結合實際案例。

2、分頁、排序、跨庫聯合查詢

分頁、排序、聯合查詢是開發中使用頻率非常高的功能,但在分庫分表後,這些看似普通的操作卻是讓人非常頭疼的問題。將分散在不同庫中表的數據查詢出來,再將所有結果進行彙總整理後提供給用戶。

3、分佈式主鍵

分庫分表後數據庫的自增主鍵意義就不大了,因爲我們不能依靠單個數據庫實例上的自增主鍵來實現不同數據庫之間的全局唯一主鍵,此時一個能夠生成全局唯一 ID 的系統是非常必要的,那麼這個全局唯一 ID 就叫 分佈式ID

4、讀寫分離

不難發現大部分主流的關係型數據庫都提供了主從架構的高可用方案,而我們需要實現 讀寫分離 + 分庫分表,讀庫與寫庫都要做分庫分表處理,後邊會有具體實戰案例。

5、數據脫敏

數據脫敏,是指對某些敏感信息通過脫敏規則進行數據轉換,從而實現敏感隱私數據的可靠保護,如身份證號、手機號、卡號、賬號密碼等個人信息,一般這些都需要進行做脫敏處理。

分庫分表工具

我還是那句話,儘量不要自己造輪子,因爲自己造的輪子可能不那麼圓,業界已經有了很多比較成熟的分庫分表中間件,我們根據自身的業務需求挑選,將更多的精力放在業務實現上。

爲什麼選 sharding-jdbc

sharding-jdbc 是一款輕量級 Java 框架,以 jar 包形式提供服務,是屬於客戶端產品不需要額外部署,它相當於是個增強版的 JDBC 驅動;相比之下像 Mycat 這類需要單獨的部署服務的服務端產品,就稍顯複雜了。況且我想把更多精力放在實現業務,不想做額外的運維工作。

不難發現確實是比較強大的一款工具,而且它對項目的侵入性很小,幾乎不用做任何代碼層的修改,也無需修改 SQL 語句,只需配置待分庫分表的數據表即可。

總結

簡單的回顧一下分庫分表的基礎知識,接下來的文章會配合實戰項目介紹 sharding-jdbc 在分庫分表中的各個功能點。


本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/rYG58KS9kHDDOMajKT9y5Q