數(shù)據(jù)庫(kù)分庫(kù)分表思路

一.數(shù)據(jù)切分

關(guān)系型數(shù)據(jù)庫(kù)本身比較容易成為系統(tǒng)瓶頸,單機(jī)存儲(chǔ)容量扎筒、連接數(shù)、處理能力都有限酬姆。當(dāng)單表的數(shù)據(jù)量達(dá)到1000W或100G以后嗜桌,由于查詢(xún)維度較多,即使添加從庫(kù)辞色、優(yōu)化索引骨宠,做很多操作時(shí)性能仍下降嚴(yán)重。此時(shí)就要考慮對(duì)其進(jìn)行切分了,切分的目的就在于減少數(shù)據(jù)庫(kù)的負(fù)擔(dān)层亿,縮短查詢(xún)時(shí)間桦卒。
數(shù)據(jù)庫(kù)分布式核心內(nèi)容無(wú)非就是數(shù)據(jù)切分(Sharding),以及切分后對(duì)數(shù)據(jù)的定位匿又、整合方灾。數(shù)據(jù)切分就是將數(shù)據(jù)分散存儲(chǔ)到多個(gè)數(shù)據(jù)庫(kù)中,使得單一數(shù)據(jù)庫(kù)中的數(shù)據(jù)量變小碌更,通過(guò)擴(kuò)充主機(jī)的數(shù)量緩解單一數(shù)據(jù)庫(kù)的性能問(wèn)題裕偿,從而達(dá)到提升數(shù)據(jù)庫(kù)操作性能的目的。
數(shù)據(jù)切分根據(jù)其切分類(lèi)型痛单,可以分為兩種方式:垂直(縱向)切分和水平(橫向)切分

1嘿棘、垂直(縱向)切分

垂直切分常見(jiàn)有垂直分庫(kù)和垂直分表兩種。

垂直分庫(kù)就是根據(jù)業(yè)務(wù)耦合性桦他,將關(guān)聯(lián)度低的不同表存儲(chǔ)在不同的數(shù)據(jù)庫(kù)蔫巩。做法與大系統(tǒng)拆分為多個(gè)小系統(tǒng)類(lèi)似,按業(yè)務(wù)分類(lèi)進(jìn)行獨(dú)立劃分快压。與"微服務(wù)治理"的做法相似圆仔,每個(gè)微服務(wù)使用單獨(dú)的一個(gè)數(shù)據(jù)庫(kù)。如圖:

image.png

垂直分表是基于數(shù)據(jù)庫(kù)中的"列"進(jìn)行蔫劣,某個(gè)表字段較多坪郭,可以新建一張擴(kuò)展表,將不經(jīng)常用或字段長(zhǎng)度較大的字段拆分出去到擴(kuò)展表中脉幢。在字段很多的情況下(例如一個(gè)大表有100多個(gè)字段)歪沃,通過(guò)"大表拆小表",更便于開(kāi)發(fā)與維護(hù)嫌松,也能避免跨頁(yè)問(wèn)題沪曙,MySQL底層是通過(guò)數(shù)據(jù)頁(yè)存儲(chǔ)的,一條記錄占用空間過(guò)大會(huì)導(dǎo)致跨頁(yè)萎羔,造成額外的性能開(kāi)銷(xiāo)液走。另外數(shù)據(jù)庫(kù)以行為單位將數(shù)據(jù)加載到內(nèi)存中,這樣表中字段長(zhǎng)度較短且訪問(wèn)頻率較高贾陷,內(nèi)存能加載更多的數(shù)據(jù)缘眶,命中率更高,減少了磁盤(pán)IO髓废,從而提升了數(shù)據(jù)庫(kù)性能巷懈。

image.png

垂直切分的優(yōu)點(diǎn):

  • 解決業(yè)務(wù)系統(tǒng)層面的耦合,業(yè)務(wù)清晰
  • 與微服務(wù)的治理類(lèi)似慌洪,也能對(duì)不同業(yè)務(wù)的數(shù)據(jù)進(jìn)行分級(jí)管理顶燕、維護(hù)凑保、監(jiān)控、擴(kuò)展等
  • 高并發(fā)場(chǎng)景下割岛,垂直切分一定程度的提升IO愉适、數(shù)據(jù)庫(kù)連接數(shù)、單機(jī)硬件資源的瓶頸
    缺點(diǎn):
  • 部分表無(wú)法join癣漆,只能通過(guò)接口聚合方式解決维咸,提升了開(kāi)發(fā)的復(fù)雜度
  • 分布式事務(wù)處理復(fù)雜
  • 依然存在單表數(shù)據(jù)量過(guò)大的問(wèn)題(需要水平切分)

2、水平(橫向)切分

當(dāng)一個(gè)應(yīng)用難以再細(xì)粒度的垂直切分惠爽,或切分后數(shù)據(jù)量行數(shù)巨大癌蓖,存在單庫(kù)讀寫(xiě)、存儲(chǔ)性能瓶頸婚肆,這時(shí)候就需要進(jìn)行水平切分了租副。
水平切分分為庫(kù)內(nèi)分表和分庫(kù)分表,是根據(jù)表內(nèi)數(shù)據(jù)內(nèi)在的邏輯關(guān)系较性,將同一個(gè)表按不同的條件分散到多個(gè)數(shù)據(jù)庫(kù)或多個(gè)表中用僧,每個(gè)表中只包含一部分?jǐn)?shù)據(jù),從而使得單個(gè)表的數(shù)據(jù)量變小赞咙,達(dá)到分布式的效果责循。如圖所示:

image.png

庫(kù)內(nèi)分表只解決了單一表數(shù)據(jù)量過(guò)大的問(wèn)題,但沒(méi)有將表分布到不同機(jī)器的庫(kù)上攀操,因此對(duì)于減輕MySQL數(shù)據(jù)庫(kù)的壓力來(lái)說(shuō)院仿,幫助不是很大,大家還是競(jìng)爭(zhēng)同一個(gè)物理機(jī)的CPU速和、內(nèi)存歹垫、網(wǎng)絡(luò)IO,最好通過(guò)分庫(kù)分表來(lái)解決颠放。
水平切分的優(yōu)點(diǎn):

  • 不存在單庫(kù)數(shù)據(jù)量過(guò)大排惨、高并發(fā)的性能瓶頸,提升系統(tǒng)穩(wěn)定性和負(fù)載能力
  • 應(yīng)用端改造較小碰凶,不需要拆分業(yè)務(wù)模塊

缺點(diǎn):

  • 跨分片的事務(wù)一致性難以保證
  • 跨庫(kù)的join關(guān)聯(lián)查詢(xún)性能較差
  • 數(shù)據(jù)多次擴(kuò)展難度和維護(hù)量極大

水平切分后同一張表會(huì)出現(xiàn)在多個(gè)數(shù)據(jù)庫(kù)/表中暮芭,每個(gè)庫(kù)/表的內(nèi)容不同。幾種典型的數(shù)據(jù)分片規(guī)則為:
1痒留、根據(jù)數(shù)據(jù)范圍
按照時(shí)間區(qū)間或ID區(qū)間來(lái)切分。例如:按日期將不同月甚至是日的數(shù)據(jù)分散到不同的庫(kù)中蠢沿;將userId為19999的記錄分到第一個(gè)庫(kù)伸头,1000020000的分到第二個(gè)庫(kù),以此類(lèi)推舷蟀。某種意義上恤磷,某些系統(tǒng)中使用的"冷熱數(shù)據(jù)分離"面哼,將一些使用較少的歷史數(shù)據(jù)遷移到其他庫(kù)中,業(yè)務(wù)功能上只提供熱點(diǎn)數(shù)據(jù)的查詢(xún)扫步,也是類(lèi)似的實(shí)踐魔策。

這樣的優(yōu)點(diǎn)在于:

  • 單表大小可控
  • 天然便于水平擴(kuò)展,后期如果想對(duì)整個(gè)分片集群擴(kuò)容時(shí)河胎,只需要添加節(jié)點(diǎn)即可闯袒,無(wú)需對(duì)其他分片的數(shù)據(jù)進(jìn)行遷移
  • 使用分片字段進(jìn)行范圍查找時(shí),連續(xù)分片可快速定位分片進(jìn)行快速查詢(xún)游岳,有效避免跨分片查詢(xún)的問(wèn)題政敢。

缺點(diǎn):

  • 熱點(diǎn)數(shù)據(jù)成為性能瓶頸。連續(xù)分片可能存在數(shù)據(jù)熱點(diǎn)胚迫,例如按時(shí)間字段分片喷户,有些分片存儲(chǔ)最近時(shí)間段內(nèi)的數(shù)據(jù),可能會(huì)被頻繁的讀寫(xiě)访锻,而有些分片存儲(chǔ)的歷史數(shù)據(jù)褪尝,則很少被查詢(xún)


    image.png

2、根據(jù)數(shù)值取模
一般采用hash取模mod的切分方式期犬,例如:將 Customer 表根據(jù) cusno 字段切分到4個(gè)庫(kù)中河哑,余數(shù)為0的放到第一個(gè)庫(kù),余數(shù)為1的放到第二個(gè)庫(kù)哭懈,以此類(lèi)推灾馒。這樣同一個(gè)用戶(hù)的數(shù)據(jù)會(huì)分散到同一個(gè)庫(kù)中,如果查詢(xún)條件帶有cusno字段遣总,則可明確定位到相應(yīng)庫(kù)去查詢(xún)睬罗。
優(yōu)點(diǎn):

  • 數(shù)據(jù)分片相對(duì)比較均勻,不容易出現(xiàn)熱點(diǎn)和并發(fā)訪問(wèn)的瓶頸

缺點(diǎn):

  • 后期分片集群擴(kuò)容時(shí)旭斥,需要遷移舊的數(shù)據(jù)(使用一致性hash算法能較好的避免這個(gè)問(wèn)題)
  • 容易面臨跨分片查詢(xún)的復(fù)雜問(wèn)題容达。比如上例中,如果頻繁用到的查詢(xún)條件中不帶cusno時(shí)垂券,將會(huì)導(dǎo)致無(wú)法定位數(shù)據(jù)庫(kù)花盐,從而需要同時(shí)向4個(gè)庫(kù)發(fā)起查詢(xún),再在內(nèi)存中合并數(shù)據(jù)菇爪,取最小集返回給應(yīng)用算芯,分庫(kù)反而成為拖累。


    image.png

二.分庫(kù)分表帶來(lái)的問(wèn)題

分庫(kù)分表能有效的環(huán)節(jié)單機(jī)和單庫(kù)帶來(lái)的性能瓶頸和壓力凳宙,突破網(wǎng)絡(luò)IO熙揍、硬件資源、連接數(shù)的瓶頸氏涩,同時(shí)也帶來(lái)了一些問(wèn)題届囚。下面將描述這些技術(shù)挑戰(zhàn)以及對(duì)應(yīng)的解決思路有梆。

1、事務(wù)一致性問(wèn)題

分布式事務(wù)
當(dāng)更新內(nèi)容同時(shí)分布在不同庫(kù)中意系,不可避免會(huì)帶來(lái)跨庫(kù)事務(wù)問(wèn)題泥耀。跨分片事務(wù)也是分布式事務(wù)蛔添,沒(méi)有簡(jiǎn)單的方案痰催,一般可使用"XA協(xié)議"和"兩階段提交"處理。
分布式事務(wù)能最大限度保證了數(shù)據(jù)庫(kù)操作的原子性作郭。但在提交事務(wù)時(shí)需要協(xié)調(diào)多個(gè)節(jié)點(diǎn)陨囊,推后了提交事務(wù)的時(shí)間點(diǎn),延長(zhǎng)了事務(wù)的執(zhí)行時(shí)間夹攒。導(dǎo)致事務(wù)在訪問(wèn)共享資源時(shí)發(fā)生沖突或死鎖的概率增高蜘醋。隨著數(shù)據(jù)庫(kù)節(jié)點(diǎn)的增多,這種趨勢(shì)會(huì)越來(lái)越嚴(yán)重咏尝,從而成為系統(tǒng)在數(shù)據(jù)庫(kù)層面上水平擴(kuò)展的枷鎖压语。
最終一致性
對(duì)于那些性能要求很高,但對(duì)一致性要求不高的系統(tǒng)编检,往往不苛求系統(tǒng)的實(shí)時(shí)一致性胎食,只要在允許的時(shí)間段內(nèi)達(dá)到最終一致性即可,可采用事務(wù)補(bǔ)償?shù)姆绞皆识Ec事務(wù)在執(zhí)行中發(fā)生錯(cuò)誤后立即回滾的方式不同厕怜,事務(wù)補(bǔ)償是一種事后檢查補(bǔ)救的措施,一些常見(jiàn)的實(shí)現(xiàn)方法有:對(duì)數(shù)據(jù)進(jìn)行對(duì)賬檢查蕾总,基于日志進(jìn)行對(duì)比粥航,定期同標(biāo)準(zhǔn)數(shù)據(jù)來(lái)源進(jìn)行同步等等。事務(wù)補(bǔ)償還要結(jié)合業(yè)務(wù)系統(tǒng)來(lái)考慮生百。

2递雀、跨節(jié)點(diǎn)關(guān)聯(lián)查詢(xún)join問(wèn)題

切分之前,系統(tǒng)中很多列表和詳情頁(yè)所需的數(shù)據(jù)可以通過(guò)sql join來(lái)完成蚀浆。而切分之后缀程,數(shù)據(jù)可能分布在不同的節(jié)點(diǎn)上,此時(shí)join帶來(lái)的問(wèn)題就比較麻煩了市俊,考慮到性能杨凑,盡量避免使用join查詢(xún)。
解決這個(gè)問(wèn)題的一些方法:
1)全局表
全局表摆昧,也可看做是"數(shù)據(jù)字典表"撩满,就是系統(tǒng)中所有模塊都可能依賴(lài)的一些表,為了避免跨庫(kù)join查詢(xún),可以將這類(lèi)表在每個(gè)數(shù)據(jù)庫(kù)中都保存一份鹦牛。這些數(shù)據(jù)通常很少會(huì)進(jìn)行修改,所以也不擔(dān)心一致性的問(wèn)題勇吊。

2)字段冗余
一種典型的反范式設(shè)計(jì)曼追,利用空間換時(shí)間,為了性能而避免join查詢(xún)汉规。例如:訂單表保存userId時(shí)候礼殊,也將userName冗余保存一份,這樣查詢(xún)訂單詳情時(shí)就不需要再去查詢(xún)"買(mǎi)家user表"了针史。
但這種方法適用場(chǎng)景也有限晶伦,比較適用于依賴(lài)字段比較少的情況。而冗余字段的數(shù)據(jù)一致性也較難保證啄枕,就像上面訂單表的例子婚陪,買(mǎi)家修改了userName后,是否需要在歷史訂單中同步更新呢频祝?這也要結(jié)合實(shí)際業(yè)務(wù)場(chǎng)景進(jìn)行考慮泌参。

3)數(shù)據(jù)組裝
在系統(tǒng)層面,分兩次查詢(xún)常空,第一次查詢(xún)的結(jié)果集中找出關(guān)聯(lián)數(shù)據(jù)id沽一,然后根據(jù)id發(fā)起第二次請(qǐng)求得到關(guān)聯(lián)數(shù)據(jù)。最后將獲得到的數(shù)據(jù)進(jìn)行字段拼裝漓糙。

4)ER分片
關(guān)系型數(shù)據(jù)庫(kù)中铣缠,如果可以先確定表之間的關(guān)聯(lián)關(guān)系,并將那些存在關(guān)聯(lián)關(guān)系的表記錄存放在同一個(gè)分片上昆禽,那么就能較好的避免跨分片join問(wèn)題蝗蛙。在1:1或1:n的情況下,通常按照主表的ID主鍵切分为狸。如下圖所示:

image.png

這樣一來(lái)歼郭,Data Node1上面的order訂單表與orderdetail訂單詳情表就可以通過(guò)orderId進(jìn)行局部的關(guān)聯(lián)查詢(xún)了,Data Node2上也一樣辐棒。

3病曾、跨界店分頁(yè)、排序漾根、函數(shù)問(wèn)題

跨節(jié)點(diǎn)多庫(kù)進(jìn)行查詢(xún)時(shí)泰涂,會(huì)出現(xiàn)limit分頁(yè)、order by排序等問(wèn)題辐怕。分頁(yè)需要按照指定字段進(jìn)行排序逼蒙,當(dāng)排序字段就是分片字段時(shí),通過(guò)分片規(guī)則就比較容易定位到指定的分片寄疏;當(dāng)排序字段非分片字段時(shí)是牢,就變得比較復(fù)雜了僵井。需要先在不同的分片節(jié)點(diǎn)中將數(shù)據(jù)進(jìn)行排序并返回,然后將不同分片返回的結(jié)果集進(jìn)行匯總和再次排序驳棱,最終返回給用戶(hù)批什。如圖所示:

image.png

上圖中只是取第一頁(yè)的數(shù)據(jù),對(duì)性能影響還不是很大社搅。但是如果取得頁(yè)數(shù)很大驻债,情況則變得復(fù)雜很多,因?yàn)楦鞣制?jié)點(diǎn)中的數(shù)據(jù)可能是隨機(jī)的形葬,為了排序的準(zhǔn)確性合呐,需要將所有節(jié)點(diǎn)的前N頁(yè)數(shù)據(jù)都排序好做合并,最后再進(jìn)行整體的排序笙以,這樣的操作時(shí)很耗費(fèi)CPU和內(nèi)存資源的淌实,所以頁(yè)數(shù)越大,系統(tǒng)的性能也會(huì)越差猖腕。
在使用Max翩伪、Min、Sum谈息、Count之類(lèi)的函數(shù)進(jìn)行計(jì)算的時(shí)候缘屹,也需要先在每個(gè)分片上執(zhí)行相應(yīng)的函數(shù),然后將各個(gè)分片的結(jié)果集進(jìn)行匯總和再次計(jì)算侠仇,最終將結(jié)果返回轻姿。如圖所示:

image.png
4、全局主鍵避重問(wèn)題

在分庫(kù)分表環(huán)境中逻炊,由于表中數(shù)據(jù)同時(shí)存在不同數(shù)據(jù)庫(kù)中互亮,主鍵值平時(shí)使用的自增長(zhǎng)將無(wú)用武之地,某個(gè)分區(qū)數(shù)據(jù)庫(kù)自生成的ID無(wú)法保證全局唯一余素。因此需要單獨(dú)設(shè)計(jì)全局主鍵豹休,以避免跨庫(kù)主鍵重復(fù)問(wèn)題。有一些常見(jiàn)的主鍵生成策略:
1)UUID
UUID標(biāo)準(zhǔn)形式包含32個(gè)16進(jìn)制數(shù)字桨吊,分為5段威根,形式為8-4-4-4-12的36個(gè)字符,例如:550e8400-e29b-41d4-a716-446655440000
UUID是主鍵是最簡(jiǎn)單的方案视乐,本地生成洛搀,性能高,沒(méi)有網(wǎng)絡(luò)耗時(shí)佑淀。但缺點(diǎn)也很明顯留美,由于UUID非常長(zhǎng),會(huì)占用大量的存儲(chǔ)空間;另外谎砾,作為主鍵建立索引和基于索引進(jìn)行查詢(xún)時(shí)都會(huì)存在性能問(wèn)題逢倍,在InnoDB下,UUID的無(wú)序性會(huì)引起數(shù)據(jù)位置頻繁變動(dòng)景图,導(dǎo)致分頁(yè)瓶堕。

2)結(jié)合數(shù)據(jù)庫(kù)維護(hù)主鍵ID表
在數(shù)據(jù)庫(kù)中建立 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字段設(shè)置為唯一索引,同一stub值在sequence表中只有一條記錄症歇,可以同時(shí)為多張表生成全局ID。sequence表的內(nèi)容谭梗,如下所示:

+-------------------+------+
| id | stub |
+-------------------+------+
| 72157623227190423 | a |
+-------------------+------+

使用 MyISAM 存儲(chǔ)引擎而不是 InnoDB忘晤,以獲取更高的性能。MyISAM使用的是表級(jí)別的鎖激捏,對(duì)表的讀寫(xiě)是串行的设塔,所以不用擔(dān)心在并發(fā)時(shí)兩次讀取同一個(gè)ID值。
當(dāng)需要全局唯一的64位ID時(shí)远舅,執(zhí)行:

REPLACE INTO sequence (stub) VALUES ('a');
SELECT LAST_INSERT_ID();

這兩條語(yǔ)句是Connection級(jí)別的闰蛔,select last_insert_id() 必須與 replace into 在同一數(shù)據(jù)庫(kù)連接下才能得到剛剛插入的新ID。
使用replace into代替insert into好處是避免了表行數(shù)過(guò)大图柏,不需要另外定期清理序六。
此方案較為簡(jiǎn)單,但缺點(diǎn)也明顯:存在單點(diǎn)問(wèn)題蚤吹,強(qiáng)依賴(lài)DB例诀,當(dāng)DB異常時(shí),整個(gè)系統(tǒng)都不可用裁着。配置主從可以增加可用性繁涂,但當(dāng)主庫(kù)掛了,主從切換時(shí)二驰,數(shù)據(jù)一致性在特殊情況下難以保證扔罪。另外性能瓶頸限制在單臺(tái)MySQL的讀寫(xiě)性能。

flickr團(tuán)隊(duì)使用的一種主鍵生成策略桶雀,與上面的sequence表方案類(lèi)似矿酵,但更好的解決了單點(diǎn)和性能瓶頸的問(wèn)題。

這一方案的整體思想是:建立2個(gè)以上的全局ID生成的服務(wù)器矗积,每個(gè)服務(wù)器上只部署一個(gè)數(shù)據(jù)庫(kù)坏瘩,每個(gè)庫(kù)有一張sequence表用于記錄當(dāng)前全局ID。表中ID增長(zhǎng)的步長(zhǎng)是庫(kù)的數(shù)量漠魏,起始值依次錯(cuò)開(kāi)倔矾,這樣能將ID的生成散列到各個(gè)數(shù)據(jù)庫(kù)上。如下圖所示:

image.png

由兩個(gè)數(shù)據(jù)庫(kù)服務(wù)器生成ID,設(shè)置不同的auto_increment值哪自。第一臺(tái)sequence的起始值為1丰包,每次步長(zhǎng)增長(zhǎng)2,另一臺(tái)的sequence起始值為2壤巷,每次步長(zhǎng)增長(zhǎng)也是2邑彪。結(jié)果第一臺(tái)生成的ID都是奇數(shù)(1, 3, 5, 7 ...),第二臺(tái)生成的ID都是偶數(shù)(2, 4, 6, 8 ...)胧华。

這種方案將生成ID的壓力均勻分布在兩臺(tái)機(jī)器上寄症。同時(shí)提供了系統(tǒng)容錯(cuò),第一臺(tái)出現(xiàn)了錯(cuò)誤矩动,可以自動(dòng)切換到第二臺(tái)機(jī)器上獲取ID有巧。但有以下幾個(gè)缺點(diǎn):系統(tǒng)添加機(jī)器,水平擴(kuò)展時(shí)較復(fù)雜悲没;每次獲取ID都要讀寫(xiě)一次DB篮迎,DB的壓力還是很大,只能靠堆機(jī)器來(lái)提升性能示姿。

可以基于flickr的方案繼續(xù)優(yōu)化甜橱,使用批量的方式降低數(shù)據(jù)庫(kù)的寫(xiě)壓力,每次獲取一段區(qū)間的ID號(hào)段栈戳,用完之后再去數(shù)據(jù)庫(kù)獲取岂傲,可以大大減輕數(shù)據(jù)庫(kù)的壓力。如下圖所示:

image.png

還是使用兩臺(tái)DB保證可用性子檀,數(shù)據(jù)庫(kù)中只存儲(chǔ)當(dāng)前的最大ID譬胎。ID生成服務(wù)每次批量拉取6個(gè)ID,先將max_id修改為5命锄,當(dāng)應(yīng)用訪問(wèn)ID生成服務(wù)時(shí)堰乔,就不需要訪問(wèn)數(shù)據(jù)庫(kù),從號(hào)段緩存中依次派發(fā)05的ID脐恩。當(dāng)這些ID發(fā)完后镐侯,再將max_id修改為11,下次就能派發(fā)611的ID驶冒。于是苟翻,數(shù)據(jù)庫(kù)的壓力降低為原來(lái)的1/6。

3)Snowflake分布式自增ID算法
Twitter的snowflake算法解決了分布式系統(tǒng)生成全局ID的需求骗污,生成64位的Long型數(shù)字崇猫,組成部分:

  • 第一位未使用
  • 接下來(lái)41位是毫秒級(jí)時(shí)間,41位的長(zhǎng)度可以表示69年的時(shí)間
  • 5位datacenterId需忿,5位workerId诅炉。10位的長(zhǎng)度最多支持部署1024個(gè)節(jié)點(diǎn)
  • 最后12位是毫秒內(nèi)的計(jì)數(shù)蜡歹,12位的計(jì)數(shù)順序號(hào)支持每個(gè)節(jié)點(diǎn)每毫秒產(chǎn)生4096個(gè)ID序列


    image.png

這樣的好處是:毫秒數(shù)在高位,生成的ID整體上按時(shí)間趨勢(shì)遞增涕烧;不依賴(lài)第三方系統(tǒng)月而,穩(wěn)定性和效率較高,理論上QPS約為409.6w/s(1000*2^12)议纯,并且整個(gè)分布式系統(tǒng)內(nèi)不會(huì)產(chǎn)生ID碰撞父款;可根據(jù)自身業(yè)務(wù)靈活分配bit位。

不足就在于:強(qiáng)依賴(lài)機(jī)器時(shí)鐘瞻凤,如果時(shí)鐘回?fù)芎┰埽瑒t可能導(dǎo)致生成ID重復(fù)。

結(jié)合數(shù)據(jù)庫(kù)和snowflake的唯一ID方案阀参,可以參考業(yè)界較為成熟的解法:Leaf——美團(tuán)點(diǎn)評(píng)分布式ID生成系統(tǒng)肝集,并考慮到了高可用、容災(zāi)结笨、分布式下時(shí)鐘等問(wèn)題。

5湿镀、數(shù)據(jù)遷移炕吸、擴(kuò)容問(wèn)題

當(dāng)業(yè)務(wù)高速發(fā)展,面臨性能和存儲(chǔ)的瓶頸時(shí)勉痴,才會(huì)考慮分片設(shè)計(jì)赫模,此時(shí)就不可避免的需要考慮歷史數(shù)據(jù)遷移的問(wèn)題。一般做法是先讀出歷史數(shù)據(jù)蒸矛,然后按指定的分片規(guī)則再將數(shù)據(jù)寫(xiě)入到各個(gè)分片節(jié)點(diǎn)中瀑罗。此外還需要根據(jù)當(dāng)前的數(shù)據(jù)量和QPS,以及業(yè)務(wù)發(fā)展的速度雏掠,進(jìn)行容量規(guī)劃斩祭,推算出大概需要多少分片(一般建議單個(gè)分片上的單表數(shù)據(jù)量不超過(guò)1000W)

如果采用數(shù)值范圍分片,只需要添加節(jié)點(diǎn)就可以進(jìn)行擴(kuò)容了乡话,不需要對(duì)分片數(shù)據(jù)遷移摧玫。如果采用的是數(shù)值取模分片,則考慮后期的擴(kuò)容問(wèn)題就相對(duì)比較麻煩绑青。

三.什么時(shí)候考慮拆分

下面講述一下什么時(shí)候需要考慮做數(shù)據(jù)切分诬像。

1、能不切分盡量不要切分

并不是所有表都需要進(jìn)行切分闸婴,主要還是看數(shù)據(jù)的增長(zhǎng)速度坏挠。切分后會(huì)在某種程度上提升業(yè)務(wù)的復(fù)雜度,數(shù)據(jù)庫(kù)除了承載數(shù)據(jù)的存儲(chǔ)和查詢(xún)外邪乍,協(xié)助業(yè)務(wù)更好的實(shí)現(xiàn)需求也是其重要工作之一降狠。
不到萬(wàn)不得已不用輕易使用分庫(kù)分表這個(gè)大招对竣,避免"過(guò)度設(shè)計(jì)"和"過(guò)早優(yōu)化"。分庫(kù)分表之前喊熟,不要為分而分柏肪,先盡力去做力所能及的事情,例如:升級(jí)硬件芥牌、升級(jí)網(wǎng)絡(luò)烦味、讀寫(xiě)分離、索引優(yōu)化等等壁拉。當(dāng)數(shù)據(jù)量達(dá)到單表的瓶頸時(shí)候谬俄,再考慮分庫(kù)分表。

2弃理、數(shù)據(jù)量過(guò)大溃论,正常運(yùn)維影響業(yè)務(wù)訪問(wèn)

這里說(shuō)的運(yùn)維,指:
1)對(duì)數(shù)據(jù)庫(kù)備份痘昌,如果單表太大钥勋,備份時(shí)需要大量的磁盤(pán)IO和網(wǎng)絡(luò)IO。例如1T的數(shù)據(jù)辆苔,網(wǎng)絡(luò)傳輸占50MB時(shí)候算灸,需要20000秒才能傳輸完畢,整個(gè)過(guò)程的風(fēng)險(xiǎn)都是比較高的

2)對(duì)一個(gè)很大的表進(jìn)行DDL修改時(shí)驻啤,MySQL會(huì)鎖住全表菲驴,這個(gè)時(shí)間會(huì)很長(zhǎng),這段時(shí)間業(yè)務(wù)不能訪問(wèn)此表骑冗,影響很大赊瞬。如果使用pt-online-schema-change,使用過(guò)程中會(huì)創(chuàng)建觸發(fā)器和影子表贼涩,也需要很長(zhǎng)的時(shí)間巧涧。在此操作過(guò)程中,都算為風(fēng)險(xiǎn)時(shí)間遥倦。將數(shù)據(jù)表拆分褒侧,總量減少,有助于降低這個(gè)風(fēng)險(xiǎn)谊迄。

3)大表會(huì)經(jīng)常訪問(wèn)與更新闷供,就更有可能出現(xiàn)鎖等待。將數(shù)據(jù)切分统诺,用空間換時(shí)間歪脏,變相降低訪問(wèn)壓力

3、隨著業(yè)務(wù)發(fā)展粮呢,需要對(duì)某些字段垂直拆分

舉個(gè)例子婿失,假如項(xiàng)目一開(kāi)始設(shè)計(jì)的用戶(hù)表如下:

id                 bigint          #用戶(hù)的ID
name               varchar         #用戶(hù)的名字
last_login_time    datetime        #最近登錄時(shí)間
personal_info      text            #私人信息
.....                              #其他信息字段

在項(xiàng)目初始階段钞艇,這種設(shè)計(jì)是滿(mǎn)足簡(jiǎn)單的業(yè)務(wù)需求的,也方便快速迭代開(kāi)發(fā)豪硅。而當(dāng)業(yè)務(wù)快速發(fā)展時(shí)哩照,用戶(hù)量從10w激增到10億,用戶(hù)非常的活躍懒浮,每次登錄會(huì)更新 last_login_name 字段飘弧,使得 user 表被不斷update,壓力很大砚著。而其他字段:id, name, personal_info 是不變的或很少更新的次伶,此時(shí)在業(yè)務(wù)角度,就要將 last_login_time 拆分出去稽穆,新建一個(gè) user_time 表冠王。

personal_info 屬性是更新和查詢(xún)頻率較低的,并且text字段占據(jù)了太多的空間舌镶。這時(shí)候柱彻,就要對(duì)此垂直拆分出 user_ext 表了。

4餐胀、數(shù)據(jù)量快速增長(zhǎng)

隨著業(yè)務(wù)的快速發(fā)展哟楷,單表中的數(shù)據(jù)量會(huì)持續(xù)增長(zhǎng),當(dāng)性能接近瓶頸時(shí)骂澄,就需要考慮水平切分吓蘑,做分庫(kù)分表了惕虑。此時(shí)一定要選擇合適的切分規(guī)則坟冲,提前預(yù)估好數(shù)據(jù)容量

5、安全性和可用性

雞蛋不要放在一個(gè)籃子里溃蔫。在業(yè)務(wù)層面上垂直切分健提,將不相關(guān)的業(yè)務(wù)的數(shù)據(jù)庫(kù)分隔,因?yàn)槊總€(gè)業(yè)務(wù)的數(shù)據(jù)量伟叛、訪問(wèn)量都不同私痹,不能因?yàn)橐粋€(gè)業(yè)務(wù)把數(shù)據(jù)庫(kù)搞掛而牽連到其他業(yè)務(wù)。利用水平切分统刮,當(dāng)一個(gè)數(shù)據(jù)庫(kù)出現(xiàn)問(wèn)題時(shí)紊遵,不會(huì)影響到100%的用戶(hù),每個(gè)庫(kù)只承擔(dān)業(yè)務(wù)的一部分?jǐn)?shù)據(jù)威恼,這樣整體的可用性就能提高坤检。

四.案例分析

1藏否、用戶(hù)中心業(yè)務(wù)場(chǎng)景

用戶(hù)中心是一個(gè)非常常見(jiàn)的業(yè)務(wù),主要提供用戶(hù)注冊(cè)学搜、登錄娃善、查詢(xún)/修改等功能,其核心表為:

User(uid, login_name, passwd, sex, age, nickname)

uid為用戶(hù)ID, 主鍵
login_name, passwd, sex, age, nickname, 用戶(hù)屬性

任何脫離業(yè)務(wù)的架構(gòu)設(shè)計(jì)都是耍流氓瑞佩,在進(jìn)行分庫(kù)分表前聚磺,需要對(duì)業(yè)務(wù)場(chǎng)景需求進(jìn)行梳理:

  • 用戶(hù)側(cè):前臺(tái)訪問(wèn),訪問(wèn)量較大炬丸,需要保證高可用和高一致性瘫寝。主要有兩類(lèi)需求:
    1.用戶(hù)登錄:通過(guò)login_name/phone/email查詢(xún)用戶(hù)信息,1%請(qǐng)求屬于這種類(lèi)型
    2.用戶(hù)信息查詢(xún):登錄之后御雕,通過(guò)uid來(lái)查詢(xún)用戶(hù)信息矢沿,99%請(qǐng)求屬這種類(lèi)型

  • 運(yùn)營(yíng)側(cè):后臺(tái)訪問(wèn),支持運(yùn)營(yíng)需求酸纲,按照年齡捣鲸、性別、登陸時(shí)間闽坡、注冊(cè)時(shí)間等進(jìn)行分頁(yè)的查詢(xún)栽惶。是內(nèi)部系統(tǒng),訪問(wèn)量較低疾嗅,對(duì)可用性外厂、一致性的要求不高。

2代承、水平切分方法

當(dāng)數(shù)據(jù)量越來(lái)越大時(shí)汁蝶,需要對(duì)數(shù)據(jù)庫(kù)進(jìn)行水平切分,上文描述的切分方法有"根據(jù)數(shù)值范圍"和"根據(jù)數(shù)值取模"论悴。
"根據(jù)數(shù)值范圍":以主鍵uid為劃分依據(jù)掖棉,按uid的范圍將數(shù)據(jù)水平切分到多個(gè)數(shù)據(jù)庫(kù)上。例如:user-db1存儲(chǔ)uid范圍為01000w的數(shù)據(jù)膀估,user-db2存儲(chǔ)uid范圍為1000w2000wuid數(shù)據(jù)幔亥。

  • 優(yōu)點(diǎn)是:擴(kuò)容簡(jiǎn)單,如果容量不夠察纯,只要增加新db即可帕棉。
  • 不足是:請(qǐng)求量不均勻,一般新注冊(cè)的用戶(hù)活躍度會(huì)比較高饼记,所以新的user-db2會(huì)比user-db1負(fù)載高香伴,導(dǎo)致服務(wù)器利用率不平衡

"根據(jù)數(shù)值取模":也是以主鍵uid為劃分依據(jù),按uid取模的值將數(shù)據(jù)水平切分到多個(gè)數(shù)據(jù)庫(kù)上具则。例如:user-db1存儲(chǔ)uid取模得1的數(shù)據(jù)即纲,user-db2存儲(chǔ)uid取模得0的uid數(shù)據(jù)。

  • 優(yōu)點(diǎn)是:數(shù)據(jù)量和請(qǐng)求量分布均均勻
  • 不足是:擴(kuò)容麻煩乡洼,當(dāng)容量不夠時(shí)崇裁,新增加db匕坯,需要rehash。需要考慮對(duì)數(shù)據(jù)進(jìn)行平滑的遷移拔稳。
3葛峻、非uid的查詢(xún)方法

水平切分后,對(duì)于按uid查詢(xún)的需求能很好的滿(mǎn)足巴比,可以直接路由到具體數(shù)據(jù)庫(kù)术奖。而按非uid的查詢(xún),例如login_name轻绞,就不知道具體該訪問(wèn)哪個(gè)庫(kù)了采记,此時(shí)需要遍歷所有庫(kù),性能會(huì)降低很多政勃。
對(duì)于用戶(hù)側(cè)唧龄,可以采用"建立非uid屬性到uid的映射關(guān)系"的方案;對(duì)于運(yùn)營(yíng)側(cè)奸远,可以采用"前臺(tái)與后臺(tái)分離"的方案既棺。
3.1、建立非uid屬性到uid的映射關(guān)系
1)映射關(guān)系
例如:login_name不能直接定位到數(shù)據(jù)庫(kù)懒叛,可以建立login_name→uid的映射關(guān)系丸冕,用索引表或緩存來(lái)存儲(chǔ)。當(dāng)訪問(wèn)login_name時(shí)薛窥,先通過(guò)映射表查詢(xún)出login_name對(duì)應(yīng)的uid胖烛,再通過(guò)uid定位到具體的庫(kù)。
映射表只有兩列诅迷,可以承載很多數(shù)據(jù)佩番,當(dāng)數(shù)據(jù)量過(guò)大時(shí),也可以對(duì)映射表再做水平切分竟贯。這類(lèi)kv格式的索引結(jié)構(gòu)答捕,可以很好的使用cache來(lái)優(yōu)化查詢(xún)性能逝钥,而且映射關(guān)系不會(huì)頻繁變更屑那,緩存命中率會(huì)很高。

2)基因法
分庫(kù)基因:假如通過(guò)uid分庫(kù)艘款,分為8個(gè)庫(kù)持际,采用uid%8的方式進(jìn)行路由,此時(shí)是由uid的最后3bit來(lái)決定這行User數(shù)據(jù)具體落到哪個(gè)庫(kù)上哗咆,那么這3bit可以看為分庫(kù)基因蜘欲。

上面的映射關(guān)系的方法需要額外存儲(chǔ)映射表,按非uid字段查詢(xún)時(shí)晌柬,還需要多一次數(shù)據(jù)庫(kù)或cache的訪問(wèn)姥份。如果想要消除多余的存儲(chǔ)和查詢(xún)郭脂,可以通過(guò)f函數(shù)取login_name的基因作為uid的分庫(kù)基因。生成uid時(shí)澈歉,參考上文所述的分布式唯一ID生成方案展鸡,再加上最后3位bit值=f(login_name)。當(dāng)查詢(xún)login_name時(shí)埃难,只需計(jì)算f(login_name)%8的值莹弊,就可以定位到具體的庫(kù)。不過(guò)這樣需要提前做好容量規(guī)劃涡尘,預(yù)估未來(lái)幾年的數(shù)據(jù)量需要分多少庫(kù)忍弛,要預(yù)留一定bit的分庫(kù)基因。


image.png

3.2考抄、前臺(tái)與后臺(tái)分離
對(duì)于用戶(hù)側(cè)细疚,主要需求是以單行查詢(xún)?yōu)橹鳎枰ogin_name/phone/email到uid的映射關(guān)系川梅,可以解決這些字段的查詢(xún)問(wèn)題惠昔。

而對(duì)于運(yùn)營(yíng)側(cè),很多批量分頁(yè)且條件多樣的查詢(xún)挑势,這類(lèi)查詢(xún)計(jì)算量大镇防,返回?cái)?shù)據(jù)量大,對(duì)數(shù)據(jù)庫(kù)的性能消耗較高潮饱。此時(shí)来氧,如果和用戶(hù)側(cè)公用同一批服務(wù)或數(shù)據(jù)庫(kù),可能因?yàn)楹笈_(tái)的少量請(qǐng)求香拉,占用大量數(shù)據(jù)庫(kù)資源啦扬,而導(dǎo)致用戶(hù)側(cè)訪問(wèn)性能降低或超時(shí)。

這類(lèi)業(yè)務(wù)最好采用"前臺(tái)與后臺(tái)分離"的方案凫碌,運(yùn)營(yíng)側(cè)后臺(tái)業(yè)務(wù)抽取獨(dú)立的service和db扑毡,解決和前臺(tái)業(yè)務(wù)系統(tǒng)的耦合。由于運(yùn)營(yíng)側(cè)對(duì)可用性盛险、一致性的要求不高瞄摊,可以不訪問(wèn)實(shí)時(shí)庫(kù),而是通過(guò)binlog異步同步數(shù)據(jù)到運(yùn)營(yíng)庫(kù)進(jìn)行訪問(wèn)苦掘。在數(shù)據(jù)量很大的情況下换帜,還可以使用ES搜索引擎或Hive來(lái)滿(mǎn)足后臺(tái)復(fù)雜的查詢(xún)方式。

五.支持分庫(kù)分表中間件

站在巨人的肩膀上能省力很多鹤啡,目前分庫(kù)分表已經(jīng)有一些較為成熟的開(kāi)源解決方案:

六.參考

數(shù)據(jù)庫(kù)分布式架構(gòu)掃盲——分庫(kù)分表(及銀行核心系統(tǒng)適用性思考)
分庫(kù)分表的思想
水平分庫(kù)分表的關(guān)鍵步驟以及可能遇到的問(wèn)題
從原則惯驼、方案、策略及難點(diǎn)闡述分庫(kù)分表
Leaf——美團(tuán)點(diǎn)評(píng)分布式ID生成系統(tǒng)
數(shù)據(jù)庫(kù)水平切分架構(gòu)實(shí)踐-【架構(gòu)師之路】公眾號(hào)

轉(zhuǎn)載自:https://www.cnblogs.com/butterfly100/p/9034281.html
作者:butterfly100

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市祟牲,隨后出現(xiàn)的幾起案子隙畜,更是在濱河造成了極大的恐慌,老刑警劉巖说贝,帶你破解...
    沈念sama閱讀 222,590評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件禾蚕,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡狂丝,警方通過(guò)查閱死者的電腦和手機(jī)换淆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)几颜,“玉大人倍试,你說(shuō)我怎么就攤上這事〉翱蓿” “怎么了县习?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,301評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)谆趾。 經(jīng)常有香客問(wèn)我躁愿,道長(zhǎng),這世上最難降的妖魔是什么沪蓬? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,078評(píng)論 1 300
  • 正文 為了忘掉前任彤钟,我火速辦了婚禮,結(jié)果婚禮上跷叉,老公的妹妹穿的比我還像新娘逸雹。我一直安慰自己,他們只是感情好云挟,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,082評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布梆砸。 她就那樣靜靜地躺著,像睡著了一般园欣。 火紅的嫁衣襯著肌膚如雪帖世。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,682評(píng)論 1 312
  • 那天沸枯,我揣著相機(jī)與錄音日矫,去河邊找鬼。 笑死辉饱,一個(gè)胖子當(dāng)著我的面吹牛搬男,可吹牛的內(nèi)容都是我干的拣展。 我是一名探鬼主播彭沼,決...
    沈念sama閱讀 41,155評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼备埃!你這毒婦竟也來(lái)了姓惑?” 一聲冷哼從身側(cè)響起褐奴,我...
    開(kāi)封第一講書(shū)人閱讀 40,098評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎于毙,沒(méi)想到半個(gè)月后敦冬,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,638評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡唯沮,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,701評(píng)論 3 342
  • 正文 我和宋清朗相戀三年脖旱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片介蛉。...
    茶點(diǎn)故事閱讀 40,852評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡萌庆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出币旧,到底是詐尸還是另有隱情践险,我是刑警寧澤,帶...
    沈念sama閱讀 36,520評(píng)論 5 351
  • 正文 年R本政府宣布吹菱,位于F島的核電站巍虫,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏鳍刷。R本人自食惡果不足惜占遥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,181評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望输瓜。 院中可真熱鬧筷频,春花似錦、人聲如沸前痘。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,674評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)芹缔。三九已至坯癣,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間最欠,已是汗流浹背示罗。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,788評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留芝硬,地道東北人蚜点。 一個(gè)月前我還...
    沈念sama閱讀 49,279評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像拌阴,于是被迫代替她去往敵國(guó)和親绍绘。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,851評(píng)論 2 361