你好,歡迎進(jìn)入江蘇優(yōu)軟數(shù)字科技有限公司官網(wǎng)!
發(fā)布時間:2023-06-07
瀏覽次數(shù):0
點(diǎn)擊上方紅色“程序員DD”,選擇“設(shè)為星標(biāo)”
回復(fù)“資源”獲取獨(dú)家學(xué)習(xí)資料!
作者|
來源|.com//p/.html
1、數(shù)據(jù)切分
關(guān)系型數(shù)據(jù)庫本身比較容易成為系統(tǒng)困境,單機(jī)的存儲容量、連接數(shù)、處理能力都是有限的。 當(dāng)單表數(shù)據(jù)量達(dá)到1000W或100G時,由于查詢維度較多,即使添加從庫,優(yōu)化索引,進(jìn)行多次操作時,性能仍會有較大幅度的提升。 這時候就要考慮拆分了。 拆分的目的是減輕數(shù)據(jù)庫的負(fù)擔(dān),縮短查詢時間。
數(shù)據(jù)庫分布的核心內(nèi)容無非就是數(shù)據(jù)的切分(),以及切分后數(shù)據(jù)的定位與整合。 數(shù)據(jù)切分就是將數(shù)據(jù)分散存儲在多個數(shù)據(jù)庫中intellij idea 數(shù)據(jù)庫關(guān)系圖,使單個數(shù)據(jù)庫中的數(shù)據(jù)量變小。 通過擴(kuò)展主機(jī)數(shù)量,減緩單個數(shù)據(jù)庫的性能問題,從而達(dá)到提高數(shù)據(jù)庫運(yùn)行性能的目的。
數(shù)據(jù)切分按其切分類型可分為垂直(水平)切分和水平(垂直)切分兩種形式。
1.縱向(橫向)分割
垂直分片有兩種常見類型:數(shù)據(jù)庫垂直分片和表垂直分片。
垂直分片是基于業(yè)務(wù)耦合,將關(guān)聯(lián)度低的不同表存儲在不同的數(shù)據(jù)庫中。 這種做法類似于將一個大系統(tǒng)拆分成多個小系統(tǒng),按照業(yè)務(wù)分類獨(dú)立定義。 類似于“微服務(wù)治理”的方式,每個微服務(wù)使用一個單獨(dú)的數(shù)據(jù)庫。 如圖所示:
垂直表劃分是基于數(shù)據(jù)庫中的“列”。 如果某個表的數(shù)組比較多,可以創(chuàng)建一個擴(kuò)展表,將不常用或者數(shù)組厚度較大的數(shù)組拆分到擴(kuò)展表中。 在數(shù)組很多的情況下(比如一個大表有100多個數(shù)組),通過“大表拆成小表”更容易開發(fā)和維護(hù),也可以防止跨頁問題。 MySQL底層是通過數(shù)據(jù)頁來存儲的。 記錄占用的空間比會話引起的頁面?zhèn)鞑ジ?,從而?dǎo)致額外的性能開銷。 另外,數(shù)據(jù)庫以行為單位將數(shù)據(jù)加載到顯存中,這樣表中主鍵的寬度越短,訪問頻率越高,顯存可以加載的數(shù)據(jù)越多,命中率越高,并且減少了c盤的IO,從而提高了數(shù)據(jù)庫的性能。
垂直拆分的優(yōu)點(diǎn):
缺點(diǎn):
2.橫向(縱向)切分
當(dāng)應(yīng)用不能垂直分片,或者分片后數(shù)據(jù)量巨大,存在單庫讀寫和存儲性能問題時,就需要水平分片。
水平切分分為數(shù)據(jù)庫分表和分庫分表。 根據(jù)表中數(shù)據(jù)的內(nèi)在邏輯關(guān)系,將同一張表根據(jù)不同的條件分布到多個數(shù)據(jù)庫或多個表中。 每個表只包含部分?jǐn)?shù)據(jù),然后減少單個表的數(shù)據(jù)量,達(dá)到分布的效果。 如圖所示:
庫內(nèi)分表只是解決了單表數(shù)據(jù)量過大的問題,并沒有將表分布到不同機(jī)器的庫中。 所以對于減輕MySQL數(shù)據(jù)庫的壓力幫助不大。 你還在爭同一臺數(shù)學(xué)機(jī) CPU、內(nèi)存、網(wǎng)絡(luò)IO最好分庫分表解決。
水平拆分的優(yōu)點(diǎn):
缺點(diǎn):
水平拆分后,同一張表會出現(xiàn)在多個數(shù)據(jù)庫/表中,每個數(shù)據(jù)庫/表的內(nèi)容都不一樣。 幾種典型的數(shù)據(jù)分片規(guī)則是:
1.按取值范圍
按時間間隔或ID間隔分段。 例如:將不同月份甚至不同日期的數(shù)據(jù)按照日期分散到不同的庫中; 將1~9999條記錄分配到第一個庫,將10000~20000條記錄分配到第二個庫,以此類推。 從某種意義上說,在個別系統(tǒng)中使用的“冷熱數(shù)據(jù)分離”,將一些使用較少的歷史數(shù)據(jù)遷移到其他庫中,只提供業(yè)務(wù)功能的熱數(shù)據(jù)查詢,也是一種類似的做法。
這樣做的好處是:
缺點(diǎn):
2.根據(jù)值取模
通常采用散列取模的切分方法。 比如把表按照cusno數(shù)組分成4個bank,余數(shù)為0的放在第一bank,余數(shù)為1的放在第二bank。 有點(diǎn)推動。 這樣,同一個用戶的數(shù)據(jù)就會分散在同一個庫中。 如果查詢條件有cusno數(shù)組,可以明確定位到對應(yīng)的庫進(jìn)行查詢。
優(yōu)勢:
缺點(diǎn):
2.分庫分表帶來的問題
分庫分表可以有效緩解單機(jī)單庫帶來的性能困境和壓力,突破網(wǎng)絡(luò)IO、硬件資源、連接數(shù)的困境,但也帶來了一些問題。 下面介紹技術(shù)挑戰(zhàn)和相應(yīng)的解決方案。
1.交易一致性問題
分布式事務(wù)
當(dāng)更新的內(nèi)容同時分布在不同的庫中時,必然會導(dǎo)致跨庫事務(wù)問題。 跨分片交易也是分布式交易,沒有簡單的解決方案。 通??梢允褂谩癤A契約”和“兩階段提交”進(jìn)行處理。
分布式事務(wù)可以最大化數(shù)據(jù)庫操作的原子性。 但提交交易時需要多個節(jié)點(diǎn)協(xié)同,延誤了提交交易的時間點(diǎn),延長了交易的執(zhí)行時間。 這會增加事務(wù)在訪問共享資源時發(fā)生沖突或死鎖的可能性。 隨著數(shù)據(jù)庫節(jié)點(diǎn)的增加,這些趨勢會越來越嚴(yán)重,從而成為系統(tǒng)在數(shù)據(jù)庫層面水平擴(kuò)展的桎梏。
最終一致性
對于性能要求高但一致性要求低的系統(tǒng),系統(tǒng)的實(shí)時一致性往往不是很關(guān)鍵,只要在允許的時間段內(nèi)達(dá)到最終一致性即可,可以采用事務(wù)補(bǔ)償?shù)姆绞健?不同于在執(zhí)行過程中出現(xiàn)錯誤后立即回滾事務(wù)的形式,事務(wù)補(bǔ)償是一種事后檢測和修復(fù)的措施。 一些常見的實(shí)現(xiàn)方式包括:數(shù)據(jù)的對賬校驗(yàn)、基于日志的比對、定期與標(biāo)準(zhǔn)數(shù)據(jù)源比對同步等。 交易補(bǔ)償也要結(jié)合業(yè)務(wù)系統(tǒng)來考慮。
2.跨節(jié)點(diǎn)關(guān)聯(lián)查詢加入問題
拆分前,系統(tǒng)中很多列表和詳情頁需要的數(shù)據(jù),都可以通過來完成。 拆分后,數(shù)據(jù)可能分布在不同的節(jié)點(diǎn)上。 這時候join帶來的問題就比較麻煩了。 考慮到性能,盡量避免使用連接查詢。
解決這個問題的一些技巧:
1)全局表
全局表也可以看作是“數(shù)據(jù)字典表”,是系統(tǒng)中所有模塊都可能依賴的一些表。 為了防止跨庫join查詢,可以在每個庫中保存一份這樣的表。 這種數(shù)據(jù)一般很少改動,所以不怕一致性問題。
2)陣列冗余
典型的反范式設(shè)計(jì)使用空間換取時間并避免連接查詢以提高性能。 例如:保存訂單表時,也會保存一份冗余的副本,這樣查詢訂單明細(xì)時就不需要查詢“賣家用戶表”了。
但是,這些技術(shù)也適用于有限的場景,更適用于依賴數(shù)組較少的情況。 而且冗余數(shù)組的數(shù)據(jù)一致性也很難保證。 就像前面訂單表的反例,賣家變更后,是否需要在歷史訂單中同步更新? 這個也要結(jié)合實(shí)際業(yè)務(wù)場景考慮。
3)數(shù)據(jù)組裝
在系統(tǒng)層面,查詢分為兩次。 在第一次查詢的結(jié)果中找到關(guān)聯(lián)數(shù)據(jù)id,然后根據(jù)id進(jìn)行第二次請求,獲取關(guān)聯(lián)數(shù)據(jù)。 最后將得到的數(shù)據(jù)組裝成一個數(shù)組。
4) ER碎片化
在關(guān)系型數(shù)據(jù)庫中,如果能夠先確定表之間的關(guān)系,將這些相關(guān)表的記錄存儲在同一個分片上,那么就可以更好地防止跨分片連接的問題。 在1:1或者1:n的情況下,一般是根據(jù)主表的ID字段來劃分。 如右圖所示:
這樣中的訂單表和訂單明細(xì)表就可以通過偏相關(guān)的方式進(jìn)行查詢,在上也是如此。
3、跨節(jié)點(diǎn)分頁、排序、功能問題
跨節(jié)點(diǎn)、多數(shù)據(jù)庫查詢時,會出現(xiàn)limit分頁、排序等問題。 分頁需要按照指定的數(shù)組進(jìn)行排序。 當(dāng)排序數(shù)組為分片數(shù)組時,通過分片規(guī)則更容易定位到指定的分片; 當(dāng)排序數(shù)組不是分片數(shù)組時,比較復(fù)雜。 需要先對不同分片節(jié)點(diǎn)上的數(shù)據(jù)進(jìn)行排序返回,然后對不同分片返回的結(jié)果集進(jìn)行匯總重新排序,最后返回給用戶。 如圖所示:
上圖中只取了第一頁的數(shù)據(jù),對性能影響不大。 而如果獲取的page數(shù)量很大,情況就復(fù)雜很多,因?yàn)槊總€shard節(jié)點(diǎn)中的數(shù)據(jù)可能是隨機(jī)的。 為了排序的準(zhǔn)確性,需要對所有節(jié)點(diǎn)的前N頁數(shù)據(jù)進(jìn)行排序進(jìn)行合并,最后進(jìn)行整體排序,這樣的操作會消耗大量的CPU和顯存資源,所以頁數(shù)越大,系統(tǒng)性能越差。
在使用Max、Min、Sum、Count等函數(shù)進(jìn)行估算時,也需要先在每個分片上執(zhí)行相應(yīng)的函數(shù),然后對每個分片的結(jié)果集進(jìn)行匯總和重新估算,最后返回結(jié)果。 如圖所示:
4.避免全局字段重復(fù)的問題
在分庫分表環(huán)境下,由于表中的數(shù)據(jù)同時存在于不同的數(shù)據(jù)庫中,平時使用的自減字段值就沒有用了,分區(qū)數(shù)據(jù)庫自生成ID不能保證全局唯一性。 因此需要單獨(dú)設(shè)計(jì)全局字段,防止跨庫字段重復(fù)。 有一些常見的字段生成策略:
1) UUID
UUID標(biāo)準(zhǔn)方法包含32個16位補(bǔ)碼,分為5段,方法為8-4-4-4-12的36個字符,如:-e29b-41d4-a716-
UUID字段是最簡單的方案,本地生成,性能高,無網(wǎng)絡(luò)時長。 但是缺點(diǎn)也很明顯,因?yàn)閁UID很長,會占用很多存儲空間; 另外,作為字段建立索引,根據(jù)索引進(jìn)行查詢,也會有性能問題。 其次,UUID的亂序會導(dǎo)致數(shù)據(jù)位置的頻繁變化。 造成分頁。
2)結(jié)合數(shù)據(jù)庫維護(hù)字段ID表
在數(shù)據(jù)庫中建表:
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;
存根數(shù)組設(shè)置為唯一索引,同一個存根值在表中只有一條記錄,可以同時為多個表生成全局ID。 表的內(nèi)容,如下:
+-------------------+------+
| id | stub |
+-------------------+------+
| 72157623227190423 | a |
+-------------------+------+
改用存儲引擎以獲得更高的性能。 使用表級鎖,表的讀寫是串行的,不用擔(dān)心并發(fā)時讀取同一個ID值兩次。
當(dāng)需要全局唯一的64位ID時,執(zhí)行:
REPLACE?INTO?sequence?(stub)?VALUES?('a');
SELECT?LAST_INSERT_ID();
這兩句是水平的,()必須和into連接同一個數(shù)據(jù)庫才能得到剛剛插入的新ID。
使用into而不是into是為了避免表中的行數(shù)過多,不需要定期清除。
這個方案比較簡單,但是它的缺點(diǎn)也很顯著:存在單點(diǎn)問題,對DB的依賴性強(qiáng)。 當(dāng)DB異常時,整個系統(tǒng)不可用。 配置主從可以降低可用性,但是當(dāng)主庫掛掉,主從切換時,特殊情況下無法保證數(shù)據(jù)的一致性。 另外,性能困境僅限于單個MySQL的讀寫性能。
團(tuán)隊(duì)使用的一個字段生成策略和之前的表方案類似,但是更好的解決了單點(diǎn)和性能困境的問題。
本方案的總體思路是:搭建2臺以上的服務(wù)器進(jìn)行全局ID的生成,每臺服務(wù)器上只部署一個數(shù)據(jù)庫,每個數(shù)據(jù)庫都有一張表用于記錄當(dāng)前的全局ID。 ID在表中遞減的步長為數(shù)據(jù)庫個數(shù),起始值依次錯開,這樣ID的生成可以hash到每個數(shù)據(jù)庫。 如右圖所示:
ID由兩個數(shù)據(jù)庫服務(wù)器生成,設(shè)置不同的值。 第一個的起始值為1,步長每次減2,另一個的起始值為2,步長每次減2。 結(jié)果,第一臺機(jī)器生成的ID都是質(zhì)數(shù)(1,3,5,7...),第二臺機(jī)器生成的ID都是質(zhì)數(shù)(2,4,6,8.. .).
這些方案在兩臺機(jī)器上平均分配生成 ID 的壓力。 同時提供系統(tǒng)容錯能力。 如果第一臺機(jī)器出現(xiàn)錯誤,可以手動切換到第二臺機(jī)器獲取ID。 但是也有幾個缺點(diǎn):系統(tǒng)中增加機(jī)器,橫向擴(kuò)展比較復(fù)雜; 每獲取一個ID,都要對DB進(jìn)行一次讀寫,對DB的壓力還是很大的,只能通過堆機(jī)來提升性能。
大家可以在方案的基礎(chǔ)上繼續(xù)優(yōu)化,使用batch的方式來降低數(shù)據(jù)庫的寫入壓力,每次獲取一個范圍的ID號段,用完再去數(shù)據(jù)庫中獲取,可以大大降低數(shù)據(jù)庫的壓力。 如右圖所示:
仍然使用兩個DB來保證可用性,數(shù)據(jù)庫中只存儲當(dāng)前最大的ID。 ID生成服務(wù)每次批量拉取6個ID,先改成5個,應(yīng)用訪問ID生成服務(wù)時不需要訪問數(shù)據(jù)庫,從號碼段開始依次分配ID從0到5緩存。 這種ID發(fā)放后,會改為11,最后一次發(fā)放6~11的ID即可。 這樣一來,數(shù)據(jù)庫的壓力增加到原來的1/6。
3)分布式自增ID算法
該算法解決分布式系統(tǒng)生成全局ID的需求,生成64位Long數(shù)。 這些組件是:
這樣做的用處是:微秒數(shù)低,生成的ID整體按照時間趨勢遞增; 不依賴第三方系統(tǒng),穩(wěn)定性和效率高。 理論上QPS約為409.6w/s (1000*2^12) ,但整個分布式系統(tǒng)不會形成ID沖突; bits可以根據(jù)自己的業(yè)務(wù)靈活分配。
缺點(diǎn)是強(qiáng)烈依賴于機(jī)器時鐘。 如果直接撥時鐘,可能會導(dǎo)致重復(fù)生成ID。
綜上所述,結(jié)合數(shù)據(jù)庫和唯一ID方案,可以參考業(yè)界比較成熟的方案:Leaf-美團(tuán)點(diǎn)評分布式ID生成系統(tǒng),并考慮高可用、容災(zāi)、分布式時鐘等問題。
5. 數(shù)據(jù)遷移和擴(kuò)容問題
當(dāng)業(yè)務(wù)快速發(fā)展,面臨性能和存儲困難時,就會考慮分片設(shè)計(jì)。 這時候就不可避免地要考慮歷史數(shù)據(jù)遷移的問題。 通常的做法是先讀取歷史數(shù)據(jù),然后按照指定的分片規(guī)則將數(shù)據(jù)寫入各個分片節(jié)點(diǎn)。 據(jù)悉,還需要根據(jù)當(dāng)前數(shù)據(jù)量、QPS、業(yè)務(wù)發(fā)展速度進(jìn)行容量規(guī)劃,預(yù)估大概需要的分片數(shù)量(一般建議單表數(shù)據(jù)量單個分片上不應(yīng)超過 1000W)。
如果使用數(shù)值范圍分片,只需要增加節(jié)點(diǎn)擴(kuò)容,不需要遷移分片數(shù)據(jù)。 如果采用數(shù)值取模分片,考慮后期擴(kuò)容問題相對麻煩。
3. 何時考慮細(xì)分
下面描述什么時候需要考慮數(shù)據(jù)切分。
1.盡量不要分
并不是所有的表都需要拆分,主要看數(shù)據(jù)下降的速度。 切分會在一定程度上增加業(yè)務(wù)的復(fù)雜度。 數(shù)據(jù)庫不僅承載數(shù)據(jù)的存儲和查詢,輔助業(yè)務(wù)更好地實(shí)現(xiàn)需求是其重要任務(wù)之一。
除非萬不得已,否則不要使用分庫分表的大招,防止“過度設(shè)計(jì)”和“過早優(yōu)化”。 分庫分表之前,不要為了分而分,先量力而行,比如:升級硬件,升級網(wǎng)絡(luò),讀寫分離,索引優(yōu)化等。數(shù)據(jù)達(dá)到單表的困境,考慮分庫分表。
2、數(shù)據(jù)量過大,正常運(yùn)維影響業(yè)務(wù)接入
這里所說的運(yùn)維是指:
1)數(shù)據(jù)庫備份,如果單表太大,備份需要大量的C盤IO和網(wǎng)絡(luò)IO。比如1T的數(shù)據(jù),當(dāng)網(wǎng)絡(luò)傳輸占用50MB時,需要20000秒才能完成傳輸,而且整個過程風(fēng)險比較高
2)當(dāng)對大表進(jìn)行DDL更改時,MySQL會鎖住整張表,時間會很長,而且這段時間業(yè)務(wù)不能訪問這張表,影響很大。 如果使用pt---,在使用過程中會創(chuàng)建觸發(fā)器和影子表,耗時較長。 在此操作期間,它被計(jì)為風(fēng)險時間。 拆分?jǐn)?shù)據(jù)表并減少總數(shù)可以幫助降低這種風(fēng)險。
3)大表會被頻繁訪問和更新,所以更容易出現(xiàn)鎖等待。對數(shù)據(jù)進(jìn)行分段,以空間換取時間,變相降低訪問壓力
3、隨著業(yè)務(wù)發(fā)展,個別陣列需要垂直拆分
作為一個反例,如果項(xiàng)目一開始設(shè)計(jì)的user表是這樣的:
id???????????????????bigint?#用戶的ID
name varchar?#用戶的名字
last_login_time datetime?#最近登錄時間
personal_info text?#私人信息
.....?#其他信息字段
在項(xiàng)目初期,這些設(shè)計(jì)滿足簡單的業(yè)務(wù)需求,便于快速迭代開發(fā)。 業(yè)務(wù)高速發(fā)展時,用戶數(shù)量從10萬猛增到10億,用戶非常活躍。 每次登錄都會更新數(shù)組,導(dǎo)致用戶表不斷更新,壓力很大。 而其他數(shù)組:id,name,沒有變化或者很少更新。 這時候,從業(yè)務(wù)的角度來說,就需要將它們拆分出來,新建一張表。
屬性更新和查詢頻率較低,文本數(shù)組占用空間過大。 這時候就需要垂直拆分表格了。
4、數(shù)據(jù)量快速下降
隨著業(yè)務(wù)的快速發(fā)展,單表的數(shù)據(jù)量會不斷減少。 當(dāng)性能接近困境時,就要考慮水平切分,做分庫分表。 這時候就需要選擇合適的切分規(guī)則,提前預(yù)估數(shù)據(jù)容量。
5. 安全性和可用性
不要把豬肉放在一個籃子里。 在業(yè)務(wù)層面,垂直切分將不相關(guān)業(yè)務(wù)的數(shù)據(jù)庫分開。 由于每個業(yè)務(wù)的數(shù)據(jù)量和訪問量不同,不可能通過將數(shù)據(jù)庫與一個業(yè)務(wù)掛鉤來牽扯到其他業(yè)務(wù)。 通過水平分片,當(dāng)一個數(shù)據(jù)庫出現(xiàn)問題時,不會影響到100%的用戶,每個數(shù)據(jù)庫只承載部分業(yè)務(wù)數(shù)據(jù),從而提高整體的可用性。
四、案例分析 1、以用戶為中心的業(yè)務(wù)場景
用戶中心是一個很常見的業(yè)務(wù),主要提供用戶注冊、登錄、查詢/更改等功能,其核心表是:
User(uid, login_name, passwd, sex, age, nickname)
uid為用戶ID,字段
login_name, passwd, sex, age, nickname, 用戶屬性
任何脫離業(yè)務(wù)的架構(gòu)設(shè)計(jì)都是耍流氓。 分庫分表之前,需要梳理一下業(yè)務(wù)場景需求:
用戶端:前臺訪問,訪問量大,需要保證高可用和一致性。 有兩種主要類型的需求:
運(yùn)營端:后臺接入intellij idea 數(shù)據(jù)庫關(guān)系圖,支持運(yùn)營需求,根據(jù)年齡、性別、登錄時間、注冊時間等進(jìn)行分頁查詢。屬于訪問量小、可用性和一致性要求不高的內(nèi)部系統(tǒng)。
2.橫向分割法
當(dāng)數(shù)據(jù)量越來越大時,就需要對數(shù)據(jù)庫進(jìn)行水平切分。 上述的分割方法包括“按取值范圍”和“按取值??取模”。
“按取值范圍”:以字段uid為依據(jù),將數(shù)據(jù)按uid范圍橫向劃分到多個數(shù)據(jù)庫中。 例如:user-db1存儲uid范圍為0~1000w的數(shù)據(jù),user-db2存儲uid范圍為1000w~的數(shù)據(jù)。
優(yōu)點(diǎn)是:擴(kuò)容簡單,如果容量不夠,縮減新的db即可。
缺點(diǎn)是:請求量不均,通常新注冊用戶的活躍度比較高,所以new user-db2的負(fù)載會比user-db1高,導(dǎo)致服務(wù)器利用率不均衡
“按值取?!保阂彩腔谧侄蝩id來定義,根據(jù)uid取模的值將數(shù)據(jù)橫向分到多個數(shù)據(jù)庫中。 例如:user-db1存儲uid為模1的數(shù)據(jù),user-db2存儲uid為模0的uid數(shù)據(jù)。
優(yōu)點(diǎn)是:數(shù)據(jù)量和請求量分布均勻
缺點(diǎn)是:擴(kuò)容麻煩。 當(dāng)容量不夠的時候,就需要降低db。 需要考慮數(shù)據(jù)的平滑遷移。
3.非uid查詢方式
水平切分后,可以很好的滿足通過uid查詢的需求,可以直接路由到具體的數(shù)據(jù)庫。 例如,對于非 uid 查詢,您不知道訪問哪個庫。 這時候就需要遍歷所有的庫,性能會提高很多。
對于用戶端,可以采用“構(gòu)造非uid屬性與uid的映射關(guān)系”的方案; 對于運(yùn)營端,可以采用“前后臺分離”的方案。
3.1. 建立非uid屬性到uid的映射關(guān)系
1)映射關(guān)系
例如,如果不能直接定位到數(shù)據(jù)庫,可以建立一個→uid映射關(guān)系,存儲在索引表或緩存中。 訪問時先通過映射表查詢對應(yīng)的uid,再通過uid定位到具體的庫。
映射表只有兩列,可以承載很多數(shù)據(jù)。 當(dāng)數(shù)據(jù)量太大時,也可以水平拆分映射表。 這種kv格式的索引結(jié)構(gòu)可以使用緩存來優(yōu)化查詢性能,而且映射關(guān)系不會頻繁變化,緩存命中率會很高。
2)遺傳法
分庫基因:如果使用uid將數(shù)據(jù)庫分成8個庫,使用uid%8的方式進(jìn)行路由,則uid的后3位將決定這一行的User數(shù)據(jù)落入哪個庫. 那么這3位就可以看成是一個分庫基因。
其中的映射關(guān)系需要額外存儲映射表。 通過非 uid 數(shù)組查詢時,需要多一次數(shù)據(jù)庫或緩存訪問。 如果要去除冗余存儲和查詢,可以將f函數(shù)得到的基因作為uid分庫基因。 生成uid時,參考上面介紹的分布式唯一ID生成方案,加上最后3位value=f()。 查詢時,只需要估計(jì)f()%8的值,就可以定位到具體的庫。 但是,這需要提前進(jìn)行容量規(guī)劃,預(yù)估未來幾年的數(shù)據(jù)量需要分成多少個數(shù)據(jù)庫,并預(yù)留一定的分庫基因。
3.2. 前景和背景分離
對于用戶端,主要需求是單行查詢。 需要建立一個/phone/email到uid的映射關(guān)系,可以解決這個數(shù)組的查詢問題。
對于運(yùn)營端,批量分頁、各種條件的查詢非常多。 此類查詢預(yù)估量大,返回數(shù)據(jù)量大,對數(shù)據(jù)庫性能消耗大。 此時,如果同一批服務(wù)或數(shù)據(jù)庫共享給用戶側(cè),可能會因?yàn)楹笈_請求量少,占用大量數(shù)據(jù)庫資源,導(dǎo)致用戶側(cè)訪問性能下降或超時.
這類業(yè)務(wù)最好采用“前后臺分離”的方案。 運(yùn)營端后臺業(yè)務(wù)抽取一個獨(dú)立的和db來解決與前臺業(yè)務(wù)系統(tǒng)的耦合。 因?yàn)檫\(yùn)營端對可用性和一致性要求不高,所以不需要訪問實(shí)時庫,而是通過異步和同步數(shù)據(jù)訪問運(yùn)營庫。 在數(shù)據(jù)量大的情況下,也可以使用ES搜索引擎或者Hive來滿足后臺復(fù)雜的查詢方式。
5.支持分庫分表中間件
站在巨人的右臂上,能省不少力氣。 目前已經(jīng)有一些成熟的分庫分表開源方案:
6.參考
[1] 數(shù)據(jù)庫分布式架構(gòu)素養(yǎng)——分庫分表(及對建行核心系統(tǒng)適用性的思考)
[2] 分庫分表的思想
[3] 橫向分庫分表的關(guān)鍵步驟及可能出現(xiàn)的問題
[4] 從原理、方案、策略和難點(diǎn)談分庫分表
[5] Leaf——美團(tuán)點(diǎn)評分布式ID生成系統(tǒng)
[6] 建筑師之路公眾號
感謝閱讀,原創(chuàng)不易,喜歡就??點(diǎn)【在看】或【轉(zhuǎn)發(fā)同學(xué)圈】吧,這就是寫作最大的動力。
本文由轉(zhuǎn)換工具發(fā)布
關(guān)注我,回復(fù)“家群”加入各種話題討論群
我讀過了
如有侵權(quán)請聯(lián)系刪除!
Copyright ? 2023 江蘇優(yōu)軟數(shù)字科技有限公司 All Rights Reserved.正版sublime text、Codejock、IntelliJ IDEA、sketch、Mestrenova、DNAstar服務(wù)提供商
13262879759
微信二維碼