成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

1號店訂單系統(tǒng)水平分庫的實踐之路以及關(guān)鍵步驟

開發(fā) 架構(gòu)
隨著大型互聯(lián)網(wǎng)應(yīng)用的發(fā)展,海量數(shù)據(jù)的存儲和訪問成為系統(tǒng)設(shè)計的瓶頸,分布式處理成為不二選擇。數(shù)據(jù)庫拆分,特別是水平分庫是個高難度的活,涉及一系列技術(shù)決策。

隨著大型互聯(lián)網(wǎng)應(yīng)用的發(fā)展,海量數(shù)據(jù)的存儲和訪問成為系統(tǒng)設(shè)計的瓶頸,分布式處理成為不二選擇。數(shù)據(jù)庫拆分,特別是水平分庫是個高難度的活,涉及一系列技術(shù)決策。

本人有幸負(fù)責(zé)1號店訂單水平分庫的方案設(shè)計及實施落地,本人結(jié)合項目實踐,對水平分庫做一個系統(tǒng)地剖析,希望為大家水平分庫(包括去IOE)改造提供總體思路,主要內(nèi)容包括:

[[184540]]

水平分庫說明

分庫維度-- 根據(jù)哪個字段分庫

分庫策略-- 記錄如何分配到不同庫

分庫數(shù)量-- 初始庫數(shù)量及庫數(shù)量如何增長

路由透明-- 如何實現(xiàn)庫路由,支持應(yīng)用透明

分頁處理-- 跨多個庫的分頁case如何處理

Lookup映射—非分庫字段映射到分庫字段,實現(xiàn)單庫訪問

整體架構(gòu)-- 分庫的整體技術(shù)架構(gòu)

上線步驟-- 分庫改造實施上線

項目總結(jié)

水平分庫說明

數(shù)據(jù)庫拆分有兩種:

1)   垂直分庫

數(shù)據(jù)庫里的表太多,拿出部分到新的庫里,一般是根據(jù)業(yè)務(wù)劃分表,關(guān)系密切的表放同一數(shù)據(jù)庫,應(yīng)用修改數(shù)據(jù)庫連接即可,比較簡單。

2)   水平分庫

某張表太大,單個數(shù)據(jù)庫存儲不下或訪問性能有壓力,把一張表拆成多張,每張表存放部分記錄,保存在不同的數(shù)據(jù)庫里,水平分庫需要對系統(tǒng)做大的改造。

[[184542]]

1號店核心的訂單表存儲在Oracle數(shù)據(jù)庫,記錄有上億條,字段有上百個,訪問的模式也是復(fù)雜多樣,隨著業(yè)務(wù)快速增長,無論存儲空間或訪問性能都面臨巨大挑戰(zhàn),特別在大促時,訂單庫已成為系統(tǒng)瓶頸。

通常有兩種解決辦法:

Scale up,升級Oracle數(shù)據(jù)庫所在的物理機(jī),提升內(nèi)存/存儲/IO性能,但這種升級費用昂貴,并且只能滿足短期需要。

Scale out,把訂單庫拆分為多個庫,分散到多臺機(jī)器進(jìn)行存儲和訪問,這種做法支持水平擴(kuò)展,可以滿足長遠(yuǎn)需要。

1號店采取后一種做法,它的訂單庫主要包括訂單主表/訂單明細(xì)表(記錄商品明細(xì))/訂單擴(kuò)展表,水平分庫即把這3張表的記錄分到多個數(shù)據(jù)庫中,訂單水平分庫效果如下圖所示:

 

原來一個Oracle庫被多個MySQL庫取代,支持1主多備和讀寫分離,主備之間通過MySQL自帶的數(shù)據(jù)同步機(jī)制(SLA<1秒),所有應(yīng)用通過訂單服務(wù)訪問訂單數(shù)據(jù)。

分庫維度

水平分庫首先要考慮根據(jù)哪個字段作為分庫維度,選擇標(biāo)準(zhǔn)是盡量避免應(yīng)用代碼和SQL性能受影響,這就要求當(dāng)前SQL在分庫后,訪問盡量落在單個庫里,否則單庫訪問變成多庫掃描,讀寫性能和應(yīng)用邏輯都會受較大影響。

對于訂單拆分,大家首先想到的是按照用戶Id拆分,結(jié)論沒錯,但***還是數(shù)據(jù)說話,不能拍腦袋。好的做法是首先收集所有SQL,挑選where語句最常出現(xiàn)的過濾字段,比如用戶Id/訂單Id/商家Id,每個字段在SQL中有三種情況:

單Id過濾,如用戶Id=?

多Id過濾,如用戶Id IN (?,?,?)

該Id不出現(xiàn)

然后進(jìn)一步統(tǒng)計,假設(shè)共有500個SQL訪問訂單庫,3個過濾字段出現(xiàn)情況如下:

過濾字段單Id過濾多Id過濾不出現(xiàn)

用戶Id12040330

訂單Id6080360

商家Id150485

結(jié)論明顯,應(yīng)該選擇用戶Id進(jìn)行分庫。

等一等,這只是靜態(tài)分析,每個SQL訪問的次數(shù)是不一樣的,因此還要分析每個SQL的訪問量。我們分析了Top15執(zhí)行最多的SQL (它們占總執(zhí)行次數(shù)85%),如果按照用戶Id分庫,這些SQL 85%落到單個數(shù)據(jù)庫, 13%落到多個數(shù)據(jù)庫,只有2%需要遍歷所有數(shù)據(jù)庫,明顯優(yōu)于使用其他Id進(jìn)行分庫。

通過量化分析,我們知道按照用戶Id分庫是***的,同時也大致知道分庫對現(xiàn)有系統(tǒng)的影響,比如這個例子中,85%的SQL會落到單個數(shù)據(jù)庫,這部分的訪問性能會優(yōu)化,堅定了各方對分庫的信心。

分庫策略

分庫維度確定后,如何把記錄分到各個庫里呢?一般有兩種方式:

根據(jù)數(shù)值范圍,比如用戶Id為1-9999的記錄分到***個庫,10000-20000的分到第二個庫,以此類推。

根據(jù)數(shù)值取模,比如用戶Id mod n,余數(shù)為0的記錄放到***個庫,余數(shù)為1的放到第二個庫,以此類推。

兩種分法的優(yōu)劣比較如下:

評價指標(biāo)按照范圍分庫按照Mod分庫

庫數(shù)量前期數(shù)目比較小,可以隨用戶/業(yè)務(wù)按需增長前期即根據(jù)mode因子確定庫數(shù)量,數(shù)目一般比較大

訪問性能前期庫數(shù)量小,全庫查詢消耗資源少,單庫查詢性能略差前期庫數(shù)量大,全庫查詢消耗資源多,單庫查詢性能略好

調(diào)整庫數(shù)量比較容易,一般只需為新用戶增加庫,老庫拆分也只影響單個庫困難,改變mod因子導(dǎo)致數(shù)據(jù)在所有庫之間遷移

數(shù)據(jù)熱點新舊用戶購物頻率有差異,有數(shù)據(jù)熱點問題新舊用戶均勻到分布到各個庫,無熱點

實踐中,為了處理簡單,選擇mod分庫的比較多。同時二次分庫時,為了數(shù)據(jù)遷移方便,一般是按倍數(shù)增加,比如初始4個庫,二次分裂為8個,再16個。這樣對于某個庫的數(shù)據(jù),一半數(shù)據(jù)移到新庫,剩余不動,對比每次只增加一個庫,所有數(shù)據(jù)都要大規(guī)模變動。

補(bǔ)充下,mod分庫一般每個庫記錄數(shù)比較均勻,但也有些數(shù)據(jù)庫,存在超級Id,這些Id的記錄遠(yuǎn)遠(yuǎn)超過其他Id,比如在廣告場景下,某個大廣告主的廣告數(shù)可能占總體很大比例。如果按照廣告主Id取模分庫,某些庫的記錄數(shù)會特別多,對于這些超級Id,需要提供單獨庫來存儲記錄。

分庫數(shù)量

分庫數(shù)量首先和單庫能處理的記錄數(shù)有關(guān),一般來說,Mysql 單庫超過5000萬條記錄,Oracle單庫超過1億條記錄,DB壓力就很大(當(dāng)然處理能力和字段數(shù)量/訪問模式/記錄長度有進(jìn)一步關(guān)系)。

在滿足上述前提下,如果分庫數(shù)量少,達(dá)不到分散存儲和減輕DB性能壓力的目的;如果分庫的數(shù)量多,好處是每個庫記錄少,單庫訪問性能好,但對于跨多個庫的訪問,應(yīng)用程序需要訪問多個庫,如果是并發(fā)模式,要消耗寶貴的線程資源;如果是串行模式,執(zhí)行時間會急劇增加。

***分庫數(shù)量還直接影響硬件的投入,一般每個分庫跑在單獨物理機(jī)上,多一個庫意味多一臺設(shè)備。所以具體分多少個庫,要綜合評估,一般初次分庫建議分4-8個庫。

路由透明

分庫從某種意義上來說,意味著DB schema改變了,必然影響應(yīng)用,但這種改變和業(yè)務(wù)無關(guān),所以要盡量保證分庫對應(yīng)用代碼透明,分庫邏輯盡量在數(shù)據(jù)訪問層處理。當(dāng)然完全做到這一點很困難,具體哪些應(yīng)該由DAL負(fù)責(zé),哪些由應(yīng)用負(fù)責(zé),這里有一些建議:

對于單庫訪問,比如查詢條件指定用戶Id,則該SQL只需訪問特定庫。此時應(yīng)該由DAL層自動路由到特定庫,當(dāng)庫二次分裂時,也只要修改mod 因子,應(yīng)用代碼不受影響。

對于簡單的多庫查詢,DAL負(fù)責(zé)匯總各個數(shù)據(jù)庫返回的記錄,此時仍對上層應(yīng)用透明。

對于帶聚合運算的多庫查詢,如帶groupBy/orderby/min/max/avg等關(guān)鍵字,建議DAL匯總單個庫返回的結(jié)果,上層應(yīng)用做進(jìn)一步處理。一方面DAL全面支持各種case,實現(xiàn)很復(fù)雜;另一方面,從1號店實踐來看,這樣的例子不多,在上層應(yīng)用作針對性處理,更加靈活。

DAL可進(jìn)一步細(xì)分為JDBC和DAL兩層,基于JDBC層面實現(xiàn)分庫路由,系統(tǒng)開發(fā)難度大,靈活性低,目前也沒有很好的成功案例;一般是基于持久層框架進(jìn)一步封裝成DDAL(分布式數(shù)據(jù)訪問層),實現(xiàn)分庫路由,1號店DAL即基于iBatis進(jìn)行上層封裝而來。

分頁處理

分庫后,有些分頁查詢需要遍歷所有庫,這些case是分庫***的受害者L。

舉個分頁的例子,比如要求按時間順序展示某個商家的訂單,每頁100條記錄,由于是按商家查詢,需要遍歷所有數(shù)據(jù)庫,假設(shè)庫數(shù)量是8,我們來看下分頁處理邏輯:

如果取第1頁數(shù)據(jù),則需要從每個庫里按時間順序取前100條記錄,8個庫匯總后有800條,然后對這800條記錄在應(yīng)用里進(jìn)行二次排序,***取前100條。

如果取第10頁數(shù)據(jù),則需要從每個庫里取前1000(100*10)條記錄,匯總后有8000條記錄,然后對這8000條記錄二次排序后取(900,1000)條記錄。

分庫情況下,對于第k頁記錄,每個庫要多取100*(k-1)條記錄,所有庫加起來,多取的記錄更多,所以越是靠后的分頁,系統(tǒng)要耗費更多內(nèi)存和執(zhí)行時間。

對比沒分庫的情況,無論取那一頁,都只要從單個DB里取100條記錄,而且無需在應(yīng)用內(nèi)部做二次排序,非常簡單。

那如何解決分庫情況下的分頁問題呢?有以下幾種辦法:

如果是在前臺應(yīng)用提供分頁,則限定用戶只能看前面n頁,這個限制在業(yè)務(wù)上也是合理的,一般看后面的分頁意義不大(如果一定要看,可以要求用戶縮小范圍重新查詢)。

如果是后臺批處理任務(wù)要求分批獲取數(shù)據(jù),則可以加大page size,比如每次獲取5000條記錄,有效減少分頁數(shù)(當(dāng)然離線訪問一般走備庫,避免沖擊主庫)。

分庫設(shè)計時,一般還有配套大數(shù)據(jù)平臺匯總所有分庫的記錄,有些分頁查詢可以考慮走大數(shù)據(jù)平臺。

Lookup映射

分庫字段只有一個,比如這里是用戶Id,但訂單表還有其他字段可唯一區(qū)分記錄,比如訂單Id,給定一個訂單Id,相應(yīng)記錄一定在某個庫里。如果盲目地查詢所有分庫,則帶來不必要的開銷,Lookup映射可根據(jù)訂單Id,找到相應(yīng)的用戶Id,從而實現(xiàn)單庫定位。

可以事先檢索所有訂單Id和用戶Id,保存在Lookup表里,Lookup表的記錄數(shù)和訂單庫記錄總數(shù)相等,但它只有2個字段,所以存儲和查詢性能都不是問題。實際使用時,一般通過分布式緩存來優(yōu)化Lookup性能。對于新增的訂單,除了寫訂單表,同時要寫Lookup表。

整體架構(gòu)

1號店訂單水平分庫的總體技術(shù)架構(gòu)如下圖所示:

上層應(yīng)用通過訂單服務(wù)/分庫代理和DAL訪問數(shù)據(jù)庫。

代理對訂單服務(wù)實現(xiàn)功能透明,包括聚合運算,非用戶Id到用戶Id的映射。

Lookup表用于訂單Id/用戶Id映射,保證按訂單Id訪問時,可以直接落到單個庫,Cache是Lookup的內(nèi)存數(shù)據(jù)映像,提升性能,cache故障時,直接訪問Lookup表。

DAL提供庫的路由,根據(jù)用戶Id定位到某個庫,對于多庫訪問,DAL支持可選的并發(fā)訪問模式,并支持簡單記錄匯總。

Lookup表初始化數(shù)據(jù)來自于現(xiàn)有分庫數(shù)據(jù),新增記錄時,直接由代理異步寫入。

上線步驟

訂單表是核心業(yè)務(wù)表,它的水平拆分影響很多業(yè)務(wù),本身的技術(shù)改造也很大,很容易出紕漏,上線時,必須謹(jǐn)慎考慮,1號店整個方案實施過程如下:

首先實現(xiàn)Oracle和MySQL兩套庫并行,所有數(shù)據(jù)訪問指向Oracle庫,通過數(shù)據(jù)同步程序把數(shù)據(jù)從Oracle拆分到多個MySQL分庫,比如3分鐘增量同步一次。

按照上述架構(gòu)圖搭建整個體系,選擇幾個對數(shù)據(jù)實時性不高的訪問例子(如訪問歷史訂單),轉(zhuǎn)向MySQL分庫訪問,然后逐漸增加更多非實時case,以檢驗整套體系可行性。

如果性能和功能都沒問題,再一次性把所有實時讀寫訪問轉(zhuǎn)向MySQL,廢棄Oracle。

這個上線步驟多了數(shù)據(jù)同步程序的開發(fā)(大約1人周工作量,風(fēng)險很低),但分散了風(fēng)險,把***步的技術(shù)風(fēng)險(Lookup/DAL等基礎(chǔ)設(shè)施改造)和第二步的業(yè)務(wù)功能風(fēng)險(Oracle改MySQL語法)分開。1號店兩階段上線都是一次性成功,特別是第二階段上線,100多個依賴方應(yīng)用簡單重啟即完成升級,中間沒有出現(xiàn)一例較大問題。

項目總結(jié)

1號店完成訂單水平分庫的同時,把訂單庫從Oralce遷到MySQL,設(shè)備從小型機(jī)換成X86服務(wù)器,通過水平分庫和去IOE,不但支持訂單量未來增長,并且總體成本也大幅下降。

由于去IOE和訂單分庫一起實施,帶來雙重的性能影響,我們花了很大精力做性能測試,為了模擬真實場景,大家通過Tcpcopy把線上實際的查詢流量引到測試環(huán)境,先后經(jīng)過13輪的性能測試,最終6個MySQL庫相對一個Oracle,平均SQL執(zhí)行時間基本持平,性能不降低的情況下,優(yōu)化了架構(gòu),節(jié)省了成本。

對核心表做水平分庫之前,必須先做好服務(wù)化,即外部系統(tǒng)通過統(tǒng)一的訂單服務(wù)訪問相關(guān)表,不然很容易遺漏一些SQL。

1號店最終是根據(jù)用戶Id后三位取模,初始分6個庫,理論上支持多達(dá)768個庫,并且對訂單Id生成規(guī)則做了改造,使其包括用戶Id后三位,這樣新訂單Id本身包含庫定位所需信息,無需走Lookup機(jī)制,隨著老訂單歸檔到歷史庫,上述架構(gòu)中l(wèi)ookup部分可廢棄。

水平分庫是一項系統(tǒng)性工作,首先需要在理論模式指導(dǎo)下,結(jié)合實際情況,每個方面做出***選擇。其次對于特殊場景,如跨庫分頁,沒有銀彈,可以靈活處理,不走常規(guī)路。***控制好節(jié)奏,系統(tǒng)改造、數(shù)據(jù)遷移、上線實施等各個環(huán)節(jié)做好銜接,全局一盤棋。

大膽設(shè)計,小心求證,謹(jǐn)慎實施,分庫并不難。

作者介紹

王慶友,前1號店***架構(gòu)師,先后就職于ebay、騰訊、1號店等公司,精通電商業(yè)務(wù),擅長復(fù)雜系統(tǒng)業(yè)務(wù)建模和架構(gòu)分析,同時在構(gòu)建大規(guī)模的分布式系統(tǒng)方 面有豐富實踐,尤其在大型系統(tǒng)的SOA改造方面有很深入的理論和實踐,目前在尋找合作機(jī)會,微信號Brucetwins,個人公眾號”架構(gòu)之道”,歡迎一起聊架構(gòu)。隨著大型互聯(lián)網(wǎng)應(yīng)用的發(fā)展,海量數(shù)據(jù)的存儲和訪問成為系統(tǒng)設(shè)計的瓶頸,分布式處理成為不二選擇。數(shù)據(jù)庫拆分,特別是水平分庫是個高難度的活,涉及一系列技術(shù)決策。

本人有幸負(fù)責(zé)1號店訂單水平分庫的方案設(shè)計及實施落地,本人結(jié)合項目實踐,對水平分庫做一個系統(tǒng)地剖析,希望為大家水平分庫(包括去IOE)改造提供總體思路,主要內(nèi)容包括:

水平分庫說明

分庫維度-- 根據(jù)哪個字段分庫

分庫策略-- 記錄如何分配到不同庫

分庫數(shù)量-- 初始庫數(shù)量及庫數(shù)量如何增長

路由透明-- 如何實現(xiàn)庫路由,支持應(yīng)用透明

分頁處理-- 跨多個庫的分頁case如何處理

Lookup映射—非分庫字段映射到分庫字段,實現(xiàn)單庫訪問

整體架構(gòu)-- 分庫的整體技術(shù)架構(gòu)

上線步驟-- 分庫改造實施上線

項目總結(jié)

水平分庫說明

數(shù)據(jù)庫拆分有兩種:

1)   垂直分庫

數(shù)據(jù)庫里的表太多,拿出部分到新的庫里,一般是根據(jù)業(yè)務(wù)劃分表,關(guān)系密切的表放同一數(shù)據(jù)庫,應(yīng)用修改數(shù)據(jù)庫連接即可,比較簡單。

2)   水平分庫

某張表太大,單個數(shù)據(jù)庫存儲不下或訪問性能有壓力,把一張表拆成多張,每張表存放部分記錄,保存在不同的數(shù)據(jù)庫里,水平分庫需要對系統(tǒng)做大的改造。

1號店核心的訂單表存儲在Oracle數(shù)據(jù)庫,記錄有上億條,字段有上百個,訪問的模式也是復(fù)雜多樣,隨著業(yè)務(wù)快速增長,無論存儲空間或訪問性能都面臨巨大挑戰(zhàn),特別在大促時,訂單庫已成為系統(tǒng)瓶頸。

通常有兩種解決辦法:

Scale up,升級Oracle數(shù)據(jù)庫所在的物理機(jī),提升內(nèi)存/存儲/IO性能,但這種升級費用昂貴,并且只能滿足短期需要。

Scale out,把訂單庫拆分為多個庫,分散到多臺機(jī)器進(jìn)行存儲和訪問,這種做法支持水平擴(kuò)展,可以滿足長遠(yuǎn)需要。

1號店采取后一種做法,它的訂單庫主要包括訂單主表/訂單明細(xì)表(記錄商品明細(xì))/訂單擴(kuò)展表,水平分庫即把這3張表的記錄分到多個數(shù)據(jù)庫中,訂單水平分庫效果如下圖所示:

原來一個Oracle庫被多個MySQL庫取代,支持1主多備和讀寫分離,主備之間通過MySQL自帶的數(shù)據(jù)同步機(jī)制(SLA<1秒),所有應(yīng)用通過訂單服務(wù)訪問訂單數(shù)據(jù)。

分庫維度

水平分庫首先要考慮根據(jù)哪個字段作為分庫維度,選擇標(biāo)準(zhǔn)是盡量避免應(yīng)用代碼和SQL性能受影響,這就要求當(dāng)前SQL在分庫后,訪問盡量落在單個庫里,否則單庫訪問變成多庫掃描,讀寫性能和應(yīng)用邏輯都會受較大影響。

對于訂單拆分,大家首先想到的是按照用戶Id拆分,結(jié)論沒錯,但***還是數(shù)據(jù)說話,不能拍腦袋。好的做法是首先收集所有SQL,挑選where語句最常出現(xiàn)的過濾字段,比如用戶Id/訂單Id/商家Id,每個字段在SQL中有三種情況:

單Id過濾,如用戶Id=?

多Id過濾,如用戶Id IN (?,?,?)

該Id不出現(xiàn)

然后進(jìn)一步統(tǒng)計,假設(shè)共有500個SQL訪問訂單庫,3個過濾字段出現(xiàn)情況如下:

過濾字段單Id過濾多Id過濾不出現(xiàn)

用戶Id12040330

訂單Id6080360

商家Id150485

結(jié)論明顯,應(yīng)該選擇用戶Id進(jìn)行分庫。

等一等,這只是靜態(tài)分析,每個SQL訪問的次數(shù)是不一樣的,因此還要分析每個SQL的訪問量。我們分析了Top15執(zhí)行最多的SQL (它們占總執(zhí)行次數(shù)85%),如果按照用戶Id分庫,這些SQL 85%落到單個數(shù)據(jù)庫, 13%落到多個數(shù)據(jù)庫,只有2%需要遍歷所有數(shù)據(jù)庫,明顯優(yōu)于使用其他Id進(jìn)行分庫。

通過量化分析,我們知道按照用戶Id分庫是***的,同時也大致知道分庫對現(xiàn)有系統(tǒng)的影響,比如這個例子中,85%的SQL會落到單個數(shù)據(jù)庫,這部分的訪問性能會優(yōu)化,堅定了各方對分庫的信心。

分庫策略

分庫維度確定后,如何把記錄分到各個庫里呢?一般有兩種方式:

根據(jù)數(shù)值范圍,比如用戶Id為1-9999的記錄分到***個庫,10000-20000的分到第二個庫,以此類推。

根據(jù)數(shù)值取模,比如用戶Id mod n,余數(shù)為0的記錄放到***個庫,余數(shù)為1的放到第二個庫,以此類推。

兩種分法的優(yōu)劣比較如下:

評價指標(biāo)按照范圍分庫按照Mod分庫

庫數(shù)量前期數(shù)目比較小,可以隨用戶/業(yè)務(wù)按需增長前期即根據(jù)mode因子確定庫數(shù)量,數(shù)目一般比較大

訪問性能前期庫數(shù)量小,全庫查詢消耗資源少,單庫查詢性能略差前期庫數(shù)量大,全庫查詢消耗資源多,單庫查詢性能略好

調(diào)整庫數(shù)量比較容易,一般只需為新用戶增加庫,老庫拆分也只影響單個庫困難,改變mod因子導(dǎo)致數(shù)據(jù)在所有庫之間遷移

數(shù)據(jù)熱點新舊用戶購物頻率有差異,有數(shù)據(jù)熱點問題新舊用戶均勻到分布到各個庫,無熱點

實踐中,為了處理簡單,選擇mod分庫的比較多。同時二次分庫時,為了數(shù)據(jù)遷移方便,一般是按倍數(shù)增加,比如初始4個庫,二次分裂為8個,再16個。這樣對于某個庫的數(shù)據(jù),一半數(shù)據(jù)移到新庫,剩余不動,對比每次只增加一個庫,所有數(shù)據(jù)都要大規(guī)模變動。

補(bǔ)充下,mod分庫一般每個庫記錄數(shù)比較均勻,但也有些數(shù)據(jù)庫,存在超級Id,這些Id的記錄遠(yuǎn)遠(yuǎn)超過其他Id,比如在廣告場景下,某個大廣告主的廣告數(shù)可能占總體很大比例。如果按照廣告主Id取模分庫,某些庫的記錄數(shù)會特別多,對于這些超級Id,需要提供單獨庫來存儲記錄。

分庫數(shù)量

分庫數(shù)量首先和單庫能處理的記錄數(shù)有關(guān),一般來說,Mysql 單庫超過5000萬條記錄,Oracle單庫超過1億條記錄,DB壓力就很大(當(dāng)然處理能力和字段數(shù)量/訪問模式/記錄長度有進(jìn)一步關(guān)系)。

在滿足上述前提下,如果分庫數(shù)量少,達(dá)不到分散存儲和減輕DB性能壓力的目的;如果分庫的數(shù)量多,好處是每個庫記錄少,單庫訪問性能好,但對于跨多個庫的訪問,應(yīng)用程序需要訪問多個庫,如果是并發(fā)模式,要消耗寶貴的線程資源;如果是串行模式,執(zhí)行時間會急劇增加。

***分庫數(shù)量還直接影響硬件的投入,一般每個分庫跑在單獨物理機(jī)上,多一個庫意味多一臺設(shè)備。所以具體分多少個庫,要綜合評估,一般初次分庫建議分4-8個庫。

路由透明

分庫從某種意義上來說,意味著DB schema改變了,必然影響應(yīng)用,但這種改變和業(yè)務(wù)無關(guān),所以要盡量保證分庫對應(yīng)用代碼透明,分庫邏輯盡量在數(shù)據(jù)訪問層處理。當(dāng)然完全做到這一點很困難,具體哪些應(yīng)該由DAL負(fù)責(zé),哪些由應(yīng)用負(fù)責(zé),這里有一些建議:

對于單庫訪問,比如查詢條件指定用戶Id,則該SQL只需訪問特定庫。此時應(yīng)該由DAL層自動路由到特定庫,當(dāng)庫二次分裂時,也只要修改mod 因子,應(yīng)用代碼不受影響。

對于簡單的多庫查詢,DAL負(fù)責(zé)匯總各個數(shù)據(jù)庫返回的記錄,此時仍對上層應(yīng)用透明。

對于帶聚合運算的多庫查詢,如帶groupBy/orderby/min/max/avg等關(guān)鍵字,建議DAL匯總單個庫返回的結(jié)果,上層應(yīng)用做進(jìn)一步處理。一方面DAL全面支持各種case,實現(xiàn)很復(fù)雜;另一方面,從1號店實踐來看,這樣的例子不多,在上層應(yīng)用作針對性處理,更加靈活。

DAL可進(jìn)一步細(xì)分為JDBC和DAL兩層,基于JDBC層面實現(xiàn)分庫路由,系統(tǒng)開發(fā)難度大,靈活性低,目前也沒有很好的成功案例;一般是基于持久層框架進(jìn)一步封裝成DDAL(分布式數(shù)據(jù)訪問層),實現(xiàn)分庫路由,1號店DAL即基于iBatis進(jìn)行上層封裝而來。

分頁處理

分庫后,有些分頁查詢需要遍歷所有庫,這些case是分庫***的受害者L。

舉個分頁的例子,比如要求按時間順序展示某個商家的訂單,每頁100條記錄,由于是按商家查詢,需要遍歷所有數(shù)據(jù)庫,假設(shè)庫數(shù)量是8,我們來看下分頁處理邏輯:

如果取第1頁數(shù)據(jù),則需要從每個庫里按時間順序取前100條記錄,8個庫匯總后有800條,然后對這800條記錄在應(yīng)用里進(jìn)行二次排序,***取前100條。

如果取第10頁數(shù)據(jù),則需要從每個庫里取前1000(100*10)條記錄,匯總后有8000條記錄,然后對這8000條記錄二次排序后取(900,1000)條記錄。

分庫情況下,對于第k頁記錄,每個庫要多取100*(k-1)條記錄,所有庫加起來,多取的記錄更多,所以越是靠后的分頁,系統(tǒng)要耗費更多內(nèi)存和執(zhí)行時間。

對比沒分庫的情況,無論取那一頁,都只要從單個DB里取100條記錄,而且無需在應(yīng)用內(nèi)部做二次排序,非常簡單。

那如何解決分庫情況下的分頁問題呢?有以下幾種辦法:

如果是在前臺應(yīng)用提供分頁,則限定用戶只能看前面n頁,這個限制在業(yè)務(wù)上也是合理的,一般看后面的分頁意義不大(如果一定要看,可以要求用戶縮小范圍重新查詢)。

如果是后臺批處理任務(wù)要求分批獲取數(shù)據(jù),則可以加大page size,比如每次獲取5000條記錄,有效減少分頁數(shù)(當(dāng)然離線訪問一般走備庫,避免沖擊主庫)。

分庫設(shè)計時,一般還有配套大數(shù)據(jù)平臺匯總所有分庫的記錄,有些分頁查詢可以考慮走大數(shù)據(jù)平臺。

Lookup映射

分庫字段只有一個,比如這里是用戶Id,但訂單表還有其他字段可唯一區(qū)分記錄,比如訂單Id,給定一個訂單Id,相應(yīng)記錄一定在某個庫里。如果盲目地查詢所有分庫,則帶來不必要的開銷,Lookup映射可根據(jù)訂單Id,找到相應(yīng)的用戶Id,從而實現(xiàn)單庫定位。

可以事先檢索所有訂單Id和用戶Id,保存在Lookup表里,Lookup表的記錄數(shù)和訂單庫記錄總數(shù)相等,但它只有2個字段,所以存儲和查詢性能都不是問題。實際使用時,一般通過分布式緩存來優(yōu)化Lookup性能。對于新增的訂單,除了寫訂單表,同時要寫Lookup表。

整體架構(gòu)

1號店訂單水平分庫的總體技術(shù)架構(gòu)如下圖所示:

上層應(yīng)用通過訂單服務(wù)/分庫代理和DAL訪問數(shù)據(jù)庫。

代理對訂單服務(wù)實現(xiàn)功能透明,包括聚合運算,非用戶Id到用戶Id的映射。

Lookup表用于訂單Id/用戶Id映射,保證按訂單Id訪問時,可以直接落到單個庫,Cache是Lookup的內(nèi)存數(shù)據(jù)映像,提升性能,cache故障時,直接訪問Lookup表。

DAL提供庫的路由,根據(jù)用戶Id定位到某個庫,對于多庫訪問,DAL支持可選的并發(fā)訪問模式,并支持簡單記錄匯總。

Lookup表初始化數(shù)據(jù)來自于現(xiàn)有分庫數(shù)據(jù),新增記錄時,直接由代理異步寫入。

上線步驟

訂單表是核心業(yè)務(wù)表,它的水平拆分影響很多業(yè)務(wù),本身的技術(shù)改造也很大,很容易出紕漏,上線時,必須謹(jǐn)慎考慮,1號店整個方案實施過程如下:

首先實現(xiàn)Oracle和MySQL兩套庫并行,所有數(shù)據(jù)訪問指向Oracle庫,通過數(shù)據(jù)同步程序把數(shù)據(jù)從Oracle拆分到多個MySQL分庫,比如3分鐘增量同步一次。

按照上述架構(gòu)圖搭建整個體系,選擇幾個對數(shù)據(jù)實時性不高的訪問例子(如訪問歷史訂單),轉(zhuǎn)向MySQL分庫訪問,然后逐漸增加更多非實時case,以檢驗整套體系可行性。

如果性能和功能都沒問題,再一次性把所有實時讀寫訪問轉(zhuǎn)向MySQL,廢棄Oracle。

這個上線步驟多了數(shù)據(jù)同步程序的開發(fā)(大約1人周工作量,風(fēng)險很低),但分散了風(fēng)險,把***步的技術(shù)風(fēng)險(Lookup/DAL等基礎(chǔ)設(shè)施改造)和第二步的業(yè)務(wù)功能風(fēng)險(Oracle改MySQL語法)分開。1號店兩階段上線都是一次性成功,特別是第二階段上線,100多個依賴方應(yīng)用簡單重啟即完成升級,中間沒有出現(xiàn)一例較大問題。

項目總結(jié)

1號店完成訂單水平分庫的同時,把訂單庫從Oralce遷到MySQL,設(shè)備從小型機(jī)換成X86服務(wù)器,通過水平分庫和去IOE,不但支持訂單量未來增長,并且總體成本也大幅下降。

由于去IOE和訂單分庫一起實施,帶來雙重的性能影響,我們花了很大精力做性能測試,為了模擬真實場景,大家通過Tcpcopy把線上實際的查詢流量引到測試環(huán)境,先后經(jīng)過13輪的性能測試,最終6個MySQL庫相對一個Oracle,平均SQL執(zhí)行時間基本持平,性能不降低的情況下,優(yōu)化了架構(gòu),節(jié)省了成本。

對核心表做水平分庫之前,必須先做好服務(wù)化,即外部系統(tǒng)通過統(tǒng)一的訂單服務(wù)訪問相關(guān)表,不然很容易遺漏一些SQL。

1號店最終是根據(jù)用戶Id后三位取模,初始分6個庫,理論上支持多達(dá)768個庫,并且對訂單Id生成規(guī)則做了改造,使其包括用戶Id后三位,這樣新訂單Id本身包含庫定位所需信息,無需走Lookup機(jī)制,隨著老訂單歸檔到歷史庫,上述架構(gòu)中l(wèi)ookup部分可廢棄。

水平分庫是一項系統(tǒng)性工作,首先需要在理論模式指導(dǎo)下,結(jié)合實際情況,每個方面做出***選擇。其次對于特殊場景,如跨庫分頁,沒有銀彈,可以靈活處理,不走常規(guī)路。***控制好節(jié)奏,系統(tǒng)改造、數(shù)據(jù)遷移、上線實施等各個環(huán)節(jié)做好銜接,全局一盤棋。

大膽設(shè)計,小心求證,謹(jǐn)慎實施,分庫并不難。

作者介紹

王慶友,前1號店***架構(gòu)師,先后就職于ebay、騰訊、1號店等公司,精通電商業(yè)務(wù),擅長復(fù)雜系統(tǒng)業(yè)務(wù)建模和架構(gòu)分析,同時在構(gòu)建大規(guī)模的分布式系統(tǒng)方 面有豐富實踐,尤其在大型系統(tǒng)的SOA改造方面有很深入的理論和實踐,目前在尋找合作機(jī)會,微信號Brucetwins,個人公眾號”架構(gòu)之道”,歡迎一起聊架構(gòu)。

責(zé)任編輯:張燕妮 來源: 聊聊架構(gòu)
相關(guān)推薦

2020-11-18 09:39:02

MySQL數(shù)據(jù)庫SQL

2020-07-30 17:59:34

分庫分表SQL數(shù)據(jù)庫

2022-11-30 07:58:10

支付業(yè)務(wù)系統(tǒng)分庫分表

2018-01-12 15:17:40

數(shù)據(jù)庫水平分庫數(shù)據(jù)遷移

2022-10-09 18:14:31

訂單系統(tǒng)分庫分表

2013-03-06 10:54:03

云服務(wù)實踐關(guān)鍵步驟

2014-04-10 09:21:22

Windows Ser

2015-08-06 11:45:28

電商混合云運維實踐

2015-08-05 09:38:18

1號店混合云運維

2021-08-02 08:05:05

系統(tǒng)訂單 Python

2018-09-07 10:14:58

2021-02-05 10:27:23

轉(zhuǎn)型計劃項目負(fù)責(zé)人CIO

2022-07-21 14:37:12

云計算安全云架構(gòu)

2023-07-31 11:19:16

2021-11-24 14:46:06

云計算云遷移數(shù)據(jù)中心

2009-12-25 14:52:49

2023-02-15 14:09:57

云托管云退出策略

2023-12-21 11:59:29

2024-03-26 08:58:55

集成測試軟件開發(fā)Python

2019-06-12 14:34:42

云平臺云遷移云計算
點贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 亚洲精品一二三 | av一二三区 | 久久99蜜桃综合影院免费观看 | 日韩欧美国产一区二区三区 | 国产精品国产成人国产三级 | 精品成人av | 精品小视频 | 欧美成人a∨高清免费观看 老司机午夜性大片 | 国内精品一区二区 | 一区二区三区不卡视频 | 午夜精品一区二区三区三上悠亚 | 欧美日韩国产三级 | 成人一区二区三区 | 日本欧美大片 | 欧美日韩中文字幕在线 | 成人精品系列 | 97精品国产97久久久久久免费 | yiren22 亚洲综合 | 免费h在线 | 色综合网站 | 久久久久久国产精品mv | av片网| 亚洲三级av | 天天操天天玩 | 亚洲日本三级 | 伊人精品国产 | 国产真实乱全部视频 | 免费观看成人性生生活片 | 在线成人免费观看 | 神马影院一区二区三区 | 成人影 | 欧美日韩黄色一级片 | 在线观看免费毛片 | 日韩欧美在线观看视频网站 | 中文字幕av第一页 | 牛牛热在线视频 | 欧美精品1区2区3区 精品国产欧美一区二区 | 国产亚洲成av人片在线观看桃 | 久久伊人亚洲 | 日本一区二区三区在线观看 | 欧美国产精品一区二区 |