大家好颇玷,我是小富~
說在前邊
今天是《分庫分表 ShardingSphere 原理與實戰(zhàn)》系列的開篇文章笨农,之前寫過幾篇關于分庫分表的文章反響都還不錯,到現(xiàn)在公眾號:程序員小富
后臺不斷的有人留言帖渠、咨詢分庫分表的問題谒亦,我也沒想到大家對于分庫分表的話題會這么感興趣,可能很多人的工作內(nèi)容業(yè)務量較小很難接觸到這方面的技能空郊。這個系列在我腦子里籌劃了挺久的份招,奈何手說啥也不干活,就一直拖到了現(xiàn)在狞甚。
其實網(wǎng)上關于分庫分表相關的文章很多锁摔,但我還是堅持出這個系列,主要是自己學習研究哼审,順便給分享谐腰,對于一個知識孕豹,不同的人從不同的角度理解的不盡相同。
網(wǎng)上的資料看似很多十气,不過值得學有價值的得仔細挑励背,很多時候在篩選甄別的過程中,逐漸的磨滅了本就不高的學習熱情砸西。搬運抄襲雷同的東西太多叶眉,而且知識點又都比較零碎,很少有細致的原理實戰(zhàn)案例芹枷。對新手來說妥妥的從入門到放棄衅疙,即便有成體系的基本上幾篇后就斷更了(希望我不會吧!)杖狼。
我不太喜歡堆砌名詞概念炼蛤,熟悉我的朋友不難發(fā)現(xiàn)妖爷,我的文章從來都是講完原理緊跟著來一波實戰(zhàn)操作蝶涩。學習技術原理必須配合實操鞏固一下,不然三天半不到忘得干干凈凈絮识,純純的經(jīng)驗之談绿聘。
上圖是我初步羅列的ShardingSphere
提綱,在官網(wǎng)文檔基礎上補充了很多基礎知識次舌,這個系列會用幾十篇文章熄攘,詳細的梳理分庫分表基礎理論,手把手的實戰(zhàn)ShardingSphere 5.X
框架的功能和解讀源碼彼念,以及開發(fā)中容易踩坑的點挪圾,每篇附帶代碼案例demo,旨在讓新手也能看的懂逐沙,后續(xù)系列完結(jié)全部內(nèi)容會整理成PDF分享給大家哲思,期待一下吧!
話不多說吩案,咱們這就進入正題~
不急于上手實戰(zhàn)ShardingSphere
框架棚赔,先來復習下分庫分表的基礎概念,技術名詞大多晦澀難懂徘郭,不要死記硬背理解最重要靠益,當你捅破那層窗戶紙,發(fā)現(xiàn)其實它也就那么回事残揉。
什么是分庫分表
分庫分表是在海量數(shù)據(jù)下胧后,由于單庫、表數(shù)據(jù)量過大抱环,導致數(shù)據(jù)庫性能持續(xù)下降的問題壳快,演變出的技術方案途样。
分庫分表是由分庫
和分表
這兩個獨立概念組成的,只不過通常分庫與分表的操作會同時進行濒憋,以至于我們習慣性的將它們合在一起叫做分庫分表何暇。
通過一定的規(guī)則,將原本數(shù)據(jù)量大的數(shù)據(jù)庫拆分成多個單獨的數(shù)據(jù)庫凛驮,將原本數(shù)據(jù)量大的表拆分成若干個數(shù)據(jù)表裆站,使得單一的庫、表性能達到最優(yōu)的效果(響應速度快)黔夭,以此提升整體數(shù)據(jù)庫性能宏胯。
為什么分庫分表
單機數(shù)據(jù)庫的存儲能力、連接數(shù)是有限的本姥,它自身就很容易會成為系統(tǒng)的瓶頸肩袍。當單表數(shù)據(jù)量在百萬以里時,我們還可以通過添加從庫婚惫、優(yōu)化索引提升性能氛赐。
一旦數(shù)據(jù)量朝著千萬以上趨勢增長,再怎么優(yōu)化數(shù)據(jù)庫先舷,很多操作性能仍下降嚴重艰管。為了減少數(shù)據(jù)庫的負擔,提升數(shù)據(jù)庫響應速度蒋川,縮短查詢時間牲芋,這時候就需要進行分庫分表。
為什么需要分庫捺球?
容量
我們給數(shù)據(jù)庫實例分配的磁盤容量是固定的缸浦,數(shù)據(jù)量持續(xù)的大幅增長,用不了多久單機的容量就會承載不了這么多數(shù)據(jù)氮兵,解決辦法簡單粗暴裂逐,加容量!
連接數(shù)
單機的容量可以隨意擴展胆剧,但數(shù)據(jù)庫的連接數(shù)卻是有限的絮姆,在高并發(fā)場景下多個業(yè)務同時對一個數(shù)據(jù)庫操作,很容易將連接數(shù)耗盡導致too many connections
報錯秩霍,導致后續(xù)數(shù)據(jù)庫無法正常訪問篙悯。
可以通過max_connections
查看MySQL最大連接數(shù)。
show variables like '%max_connections%'
將原本單數(shù)據(jù)庫按不同業(yè)務拆分成訂單庫铃绒、物流庫鸽照、積分庫等不僅可以有效分攤數(shù)據(jù)庫讀寫壓力,也提高了系統(tǒng)容錯性颠悬。
為什么需要分表矮燎?
做過報表業(yè)務的同學應該都體驗過定血,一條SQL執(zhí)行時間超過幾十秒的場景。
導致數(shù)據(jù)庫查詢慢的原因有很多诞外,SQL沒命中索引澜沟、like掃全表、用了函數(shù)計算峡谊,這些都可以通過優(yōu)化手段解決茫虽,可唯獨數(shù)據(jù)量大是MySQL無法通過自身優(yōu)化解決的。慢的根本原因是InnoDB
存儲引擎既们,聚簇索引結(jié)構(gòu)的 B+tree 層級變高濒析,磁盤IO變多查詢性能變慢,詳細原理自行查找一下啥纸,這里不用過多篇幅說明号杏。
阿里的開發(fā)手冊中有條建議,單表行數(shù)超500萬行或者單表容量超過2GB斯棒,就推薦分庫分表盾致,然而理想和實現(xiàn)總是有差距的,阿里這種體量的公司不差錢當然可以這么用名船,實際上很多公司單表數(shù)據(jù)幾千萬绰上、億級別仍然不選擇分庫分表。
什么時候分庫分表
技術群里經(jīng)常會有小伙伴問渠驼,到底什么情況下會用分庫分表呢?
分庫分表要解決的是現(xiàn)存海量數(shù)據(jù)
訪問的性能瓶頸鉴腻,對持續(xù)激增
的數(shù)據(jù)量所做出的架構(gòu)預見性迷扇。
是否分庫分表的關鍵指標是數(shù)據(jù)量,我們以fire100.top
這個網(wǎng)站的資源表 t_resource
為例爽哎,系統(tǒng)在運行初始的時候蜓席,每天只有可憐的幾十個資源上傳,這時使用單庫课锌、單表的方式足以支持系統(tǒng)的存儲厨内,數(shù)據(jù)量小幾乎沒什么數(shù)據(jù)庫性能瓶頸。
但某天開始一股神秘的流量進入渺贤,系統(tǒng)每日產(chǎn)生的資源數(shù)據(jù)量暴增至十萬甚至上百萬級別雏胃,這時資源表數(shù)據(jù)量到達千萬級,查詢響應變得緩慢志鞍,數(shù)據(jù)庫的性能瓶頸逐漸顯現(xiàn)瞭亮。
以MySQL數(shù)據(jù)庫為例,單表的數(shù)據(jù)量在達到億條級別固棚,通過加索引统翩、SQL調(diào)優(yōu)等傳統(tǒng)優(yōu)化策略仙蚜,性能提升依舊微乎其微時,就可以考慮做分庫分表了厂汗。
既然MySQL存儲海量數(shù)據(jù)時會出現(xiàn)性能瓶頸委粉,那么我們是不是可以考慮用其他方案替代它?比如高性能的非關系型數(shù)據(jù)庫MongoDB
娶桦?
可以艳丛,但要看存儲的數(shù)據(jù)類型!
現(xiàn)在互聯(lián)網(wǎng)上大部分公司的核心數(shù)據(jù)幾乎是存儲在關系型數(shù)據(jù)庫(MySQL趟紊、Oracle等)氮双,因為它們有著NoSQL
如法比擬的穩(wěn)定性和可靠性,產(chǎn)品成熟生態(tài)系統(tǒng)完善霎匈,還有核心的事務功能特性戴差,也是其他存儲工具不具備的,而評論铛嘱、點贊這些非核心數(shù)據(jù)還是可以考慮用MongoDB
的暖释。
如何分庫分表
分庫分表的核心就是對數(shù)據(jù)的分片(
Sharding
)并相對均勻的路由在不同的庫、表中墨吓,以及分片后對數(shù)據(jù)的快速定位與檢索結(jié)果的整合球匕。
分庫與分表可以從:垂直(縱向)和 水平(橫向)兩種緯度進行拆分。下邊我們以經(jīng)典的訂單業(yè)務舉例帖烘,看看如何拆分亮曹。
垂直拆分
1、垂直分庫
垂直分庫一般來說按照業(yè)務和功能的維度進行拆分秘症,將不同業(yè)務數(shù)據(jù)分別放到不同的數(shù)據(jù)庫中照卦,核心理念 專庫專用
。
按業(yè)務類型對數(shù)據(jù)分離乡摹,剝離為多個數(shù)據(jù)庫役耕,像訂單、支付聪廉、會員瞬痘、積分相關等表放在對應的訂單庫、支付庫板熊、會員庫框全、積分庫。不同業(yè)務禁止跨庫直連邻邮,獲取對方業(yè)務數(shù)據(jù)一律通過API
接口交互竣况,這也是微服務拆分的一個重要依據(jù)。
垂直分庫很大程度上取決于業(yè)務的劃分,但有時候業(yè)務間的劃分并不是那么清晰丹泉,比如:電商中訂單數(shù)據(jù)的拆分情萤,其他很多業(yè)務都依賴于訂單數(shù)據(jù),有時候界線不是很好劃分摹恨。
垂直分庫把一個庫的壓力分攤到多個庫筋岛,提升了一些數(shù)據(jù)庫性能,但并沒有解決由于單表數(shù)據(jù)量過大導致的性能問題晒哄,所以就需要配合后邊的分表來解決睁宰。
2、垂直分表
垂直分表針對業(yè)務上字段比較多的大表進行的寝凌,一般是把業(yè)務寬表中比較獨立的字段柒傻,或者不常用的字段拆分到單獨的數(shù)據(jù)表中,是一種大表拆小表的模式较木。
例如:一張t_order
訂單表上有幾十個字段红符,其中訂單金額相關字段計算頻繁,為了不影響訂單表t_order
的性能伐债,就可以把訂單金額相關字段拆出來單獨維護一個t_order_price_expansion
擴展表预侯,這樣每張表只存儲原表的一部分字段,通過訂單號order_no
做關聯(lián)峰锁,再將拆分出來的表路由到不同的庫中萎馅。
數(shù)據(jù)庫它是以行為單位將數(shù)據(jù)加載到內(nèi)存中,這樣拆分以后核心表大多是訪問頻率較高的字段虹蒋,而且字段長度也都較短糜芳,因而可以加載更多數(shù)據(jù)到內(nèi)存中,減少磁盤IO千诬,增加索引查詢的命中率耍目,進一步提升數(shù)據(jù)庫性能。
水平拆分
上邊垂直分庫徐绑、垂直分表后還是會存在單庫、表數(shù)據(jù)量過大的問題莫辨,當我們的應用已經(jīng)無法在細粒度的垂直切分時傲茄,依舊存在單庫讀寫、存儲性能瓶頸沮榜,這時就要配合水平分庫盘榨、水平分表一起了。
1蟆融、水平分庫
水平分庫是把同一個表按一定規(guī)則拆分到不同的數(shù)據(jù)庫中草巡,每個庫可以位于不同的服務器上,以此實現(xiàn)水平擴展型酥,是一種常見的提升數(shù)據(jù)庫性能的方式山憨。
例如:db_orde_1
查乒、db_order_2
兩個數(shù)據(jù)庫內(nèi)有完全相同的t_order
表,我們在訪問某一筆訂單時可以通過對訂單的訂單編號取模的方式 訂單編號 mod 2 (數(shù)據(jù)庫實例數(shù))
郁竟,指定該訂單應該在哪個數(shù)據(jù)庫中操作玛迄。
這種方案往往能解決單庫存儲量及性能瓶頸問題,但由于同一個表被分配在不同的數(shù)據(jù)庫中棚亩,數(shù)據(jù)的訪問需要額外的路由工作蓖议,因此系統(tǒng)的復雜度也被提升了。
2讥蟆、水平分表
水平分表是在同一個數(shù)據(jù)庫內(nèi)勒虾,把一張大數(shù)據(jù)量的表按一定規(guī)則,切分成多個結(jié)構(gòu)完全相同表瘸彤,而每個表只存原表的一部分數(shù)據(jù)修然。
例如:一張t_order
訂單表有900萬數(shù)據(jù),經(jīng)過水平拆分出來三個表钧栖,t_order_1
低零、t_order_2
、t_order_3
拯杠,每張表存有數(shù)據(jù)300萬掏婶,以此類推。
水平分表盡管拆分了表潭陪,但子表都還是在同一個數(shù)據(jù)庫實例中雄妥,只是解決了單一表數(shù)據(jù)量過大的問題,并沒有將拆分后的表分散到不同的機器上依溯,還在競爭同一個物理機的CPU老厌、內(nèi)存、網(wǎng)絡IO等黎炉。要想進一步提升性能枝秤,就需要將拆分后的表分散到不同的數(shù)據(jù)庫中,達到分布式的效果慷嗜。
數(shù)據(jù)存在哪個庫的表
分庫分表以后會出現(xiàn)一個問題淀弹,一張表會出現(xiàn)在多個數(shù)據(jù)庫里,到底該往哪個庫的哪個表里存呢庆械?
上邊我們多次提到過一定規(guī)則
薇溃,其實這個規(guī)則它是一種路由算法,決定了一條數(shù)據(jù)具體應該存在哪個數(shù)據(jù)庫的哪張表里缭乘。
常見的有 取模算法
沐序、范圍限定算法
、范圍+取模算法
、預定義算法
1策幼、取模算法
關鍵字段取模(對hash結(jié)果取余數(shù) hash(XXX) mod N)邑时,N為數(shù)據(jù)庫實例數(shù)或子表數(shù)量)是最為常見的一種路由方式。
以t_order
訂單表為例垄惧,先給數(shù)據(jù)庫從 0 到 N-1進行編號刁愿,對 t_order
訂單表中order_no
訂單編號字段進行取模hash(order_no) mod N
,得到余數(shù)i
到逊。i=0
存第一個庫铣口,i=1
存第二個庫,i=2
存第三個庫觉壶,以此類推脑题。
同一筆訂單數(shù)據(jù)會落在同一個庫、表里铜靶,查詢時用相同的規(guī)則叔遂,用t_order
訂單編號作為查詢條件,就能快速的定位到數(shù)據(jù)争剿。
優(yōu)點
實現(xiàn)簡單已艰,數(shù)據(jù)分布相對比較均勻,不易出現(xiàn)請求都打到一個庫上的情況蚕苇。
缺點
取模算法對集群的伸縮支持不太友好哩掺,集群中有N個數(shù)據(jù)庫實·hash(user_id) mod N
,當某一臺機器宕機涩笤,本應該落在該數(shù)據(jù)庫的請求就無法得到處理嚼吞,這時宕掉的實例會被踢出集群。
此時機器數(shù)減少算法發(fā)生變化hash(user_id) mod N-1
蹬碧,同一用戶數(shù)據(jù)落在了在不同數(shù)據(jù)庫中舱禽,等這臺機器恢復,用user_id
作為條件查詢用戶數(shù)據(jù)就會少一部分恩沽。
2誊稚、范圍限定算法
范圍限定算法以某些范圍字段,如時間
或ID區(qū)
拆分罗心。
用戶表t_user
被拆分成t_user_1
片吊、t_user_2
、t_user_3
三張表协屡,后續(xù)將user_id
范圍為1 ~ 1000w的用戶數(shù)據(jù)放入t_user_1
,1000~ 2000w放入t_user_2
全谤,2000~3000w放入t_user_3
肤晓,以此類推。按日期范圍劃分同理。
優(yōu)點
- 單表數(shù)據(jù)量是可控的
- 水平擴展簡單只需增加節(jié)點即可补憾,無需對其他分片的數(shù)據(jù)進行遷移
缺點
- 由于連續(xù)分片可能存在
數(shù)據(jù)熱點
漫萄,比如按時間字段分片時,如果某一段時間(雙11等大促)訂單驟增盈匾,存11月數(shù)據(jù)的表可能會被頻繁的讀寫腾务,其他分片表存儲的歷史數(shù)據(jù)則很少被查詢,導致數(shù)據(jù)傾斜削饵,數(shù)據(jù)庫壓力分攤不均勻。
3、范圍 + 取模算法
為了避免熱點數(shù)據(jù)的問題约急,我們可以對上范圍算法優(yōu)化一下
這次我們先通過范圍算法定義每個庫的用戶表t_user
只存1000w數(shù)據(jù)咒彤,第一個db_order_1
庫存放userId
從1 ~ 1000w,第二個庫10002000w劈伴,第三個庫20003000w密末,以此類推。
每個庫里再把用戶表t_user
拆分成t_user_1
跛璧、t_user_2
严里、t_user_3
等,對userd
進行取模路由到對應的表中追城。
有效的避免數(shù)據(jù)分布不均勻的問題刹碾,數(shù)據(jù)庫水平擴展也簡單,直接添加實例無需遷移歷史數(shù)據(jù)漓柑。
4教硫、地理位置分片
地理位置分片其實是一個更大的范圍,按城市或者地域劃分辆布,比如華東瞬矩、華北數(shù)據(jù)放在不同的分片庫、表锋玲。
5景用、預定義算法
預定義算法是事先已經(jīng)明確知道分庫和分表的數(shù)量,可以直接將某類數(shù)據(jù)路由到指定庫或表中惭蹂,查詢的時候亦是如此伞插。
分庫分表出來的問題
了解了上邊分庫分表的拆分方式不難發(fā)現(xiàn),相比于拆分前的單庫單表盾碗,系統(tǒng)的數(shù)據(jù)存儲架構(gòu)演變到現(xiàn)在已經(jīng)變得非常復雜媚污。看幾個具有代表性的問題廷雅,比如:
分頁耗美、排序京髓、跨節(jié)點聯(lián)合查詢
分頁、排序商架、聯(lián)合查詢堰怨,這些看似普通,開發(fā)中使用頻率較高的操作蛇摸,在分庫分表后卻是讓人非常頭疼的問題备图。把分散在不同庫中表的數(shù)據(jù)查詢出來,再將所有結(jié)果進行匯總合并整理后提供給用戶赶袄。
比如:我們要查詢11揽涮、12月的訂單數(shù)據(jù),如果兩個月的數(shù)據(jù)是分散到了不同的數(shù)據(jù)庫實例弃鸦,則要查詢兩個數(shù)據(jù)庫相關的數(shù)據(jù)绞吁,在對數(shù)據(jù)合并排序、分頁唬格,過程繁瑣復雜家破。
事務一致性
分庫分表后由于表分布在不同庫中,不可避免會帶來跨庫事務問題购岗。后續(xù)會分別以阿里的Seata
和MySQL的XA
協(xié)議實現(xiàn)分布式事務汰聋,用來比較各自的優(yōu)勢與不足。
全局唯一的主鍵
分庫分表后數(shù)據(jù)庫表的主鍵ID業(yè)務意義就不大了喊积,因為無法在標識唯一一條記錄烹困,例如:多張表t_order_1
、t_order_2
的主鍵ID全部從1開始會重復乾吻,此時我們需要主動為一條記錄分配一個ID髓梅,這個全局唯一的ID就叫分布式ID
,發(fā)放這個ID的系統(tǒng)通常被叫發(fā)號器绎签。
多數(shù)據(jù)庫高效治理
對多個數(shù)據(jù)庫以及庫內(nèi)大量分片表的高效治理枯饿,是非常有必要,因為像某寶這種大廠一次大促下來诡必,訂單表可能會被拆分成成千上萬個t_order_n
表奢方,如果沒有高效的管理方案,手動建表爸舒、排查問題是一件很恐怖的事蟋字。
歷史數(shù)據(jù)遷移
分庫分表架構(gòu)落地以后,首要的問題就是如何平滑的遷移歷史數(shù)據(jù)扭勉,增量數(shù)據(jù)和全量數(shù)據(jù)遷移鹊奖,這又是一個比較麻煩的事情,后邊詳細講涂炎。
分庫分表架構(gòu)模式
分庫分表架構(gòu)主要有兩種模式:client
客戶端模式和proxy
代理模式
客戶模式
client
模式指分庫分表的邏輯都在你的系統(tǒng)應用內(nèi)部進行控制嫉入,應用會將拆分后的SQL直連多個數(shù)據(jù)庫進行操作焰盗,然后本地進行數(shù)據(jù)的合并匯總等操作。
代理模式
proxy
代理模式將應用程序與MySQL數(shù)據(jù)庫隔離咒林,業(yè)務方的應用不在需要直連數(shù)據(jù)庫,而是連接proxy代理服務爷光,代理服務實現(xiàn)了MySQL的協(xié)議垫竞,對業(yè)務方來說代理服務就是數(shù)據(jù)庫,它會將SQL分發(fā)到具體的數(shù)據(jù)庫進行執(zhí)行蛀序,并返回結(jié)果欢瞪。該服務內(nèi)有分庫分表的配置,根據(jù)配置自動創(chuàng)建分片表徐裸。
如何抉擇
如何選擇client
模式和proxy
模式遣鼓,我們可以從以下幾個方面來簡單做下比較。
1重贺、性能
性能方面client
模式表現(xiàn)的稍好一些骑祟,它是直接連接MySQL執(zhí)行命令;
proxy
代理服務則將整個執(zhí)行鏈路延長了气笙,應用->代理服務->MySQL次企,可能導致性能有一些損耗,但兩者差距并不是非常大潜圃。
2缸棵、復雜度
client
模式在開發(fā)使用通常引入一個jar可以;
proxy
代理模式則需要搭建單獨的服務谭期,有一定的維護成本堵第,既然是服務那么就要考慮高可用,畢竟應用的所有SQL都要通過它轉(zhuǎn)發(fā)至MySQL隧出。
3踏志、升級
client
模式分庫分表一般是依賴基礎架構(gòu)團隊的Jar包,一旦有版本升級或者Bug修改鸳劳,所有應用到的項目都要跟著升級狰贯。小規(guī)模的團隊服務少升級問題不大,如果是大公司服務規(guī)模大赏廓,且涉及到跨多部門涵紊,那么升級一次成本就比較高;
proxy
模式在升級方面優(yōu)勢很明顯幔摸,發(fā)布新功能或者修復Bug摸柄,只要重新部署代理服務集群即可,業(yè)務方是無感知的既忆,但要保證發(fā)布過程中服務的可用性驱负。
4嗦玖、治理、監(jiān)控
client
模式由于是內(nèi)嵌在應用內(nèi)跃脊,應用集群部署不太方便統(tǒng)一處理宇挫;proxy
模式在對SQL限流、讀寫權限控制酪术、監(jiān)控器瘪、告警等服務治理方面更優(yōu)雅一些。
結(jié)束語
本文主要是回顧一下分庫分表的一些基礎概念绘雁,為大家在后續(xù)ShardingSphere
實踐中更好上手理解橡疼,內(nèi)容里很多概念一筆帶過沒詳細展開,接下來的篇幅會逐一解讀庐舟。
下一篇預告《分庫分表ShardingSphere的基礎知識點梳理》
歡迎關注 公眾號:程序員小富欣除,咱們下期再見!