Shopee的數(shù)據(jù)庫是如何做選型的-Shopee問答電商問答
2023-01-17| 21:37|發(fā)布在分類/淘寶知識(shí)|閱讀:44
2023-01-17| 21:37|發(fā)布在分類/淘寶知識(shí)|閱讀:44
本文主題Shopee,Shopee集群,Shopee問答。
Shopee在上線了5年后,已經(jīng)成為了東南亞跨境電商的領(lǐng)頭羊,該平臺(tái)的數(shù)據(jù)庫,值得不少的跨境電商平臺(tái)方學(xué)習(xí),他們是如何做選型的?幕思城電商在這里跟大家來講解一下吧。
Shopee于2015年底上線,是東南亞地區(qū)領(lǐng)先的電子商務(wù)平臺(tái),覆蓋東南亞和臺(tái)灣等多個(gè)市場(chǎng),在深圳和新加坡分別設(shè)有研發(fā)中心。本文系Shopee的分布式數(shù)據(jù)庫選型思路漫談。因?yàn)槭恰郝劇?,可能不成體系,但會(huì)著重介紹一些經(jīng)驗(yàn)以及踩過的坑,提供給大家參考。
1Shopee的數(shù)據(jù)庫使用情況
Shopee在用哪些數(shù)據(jù)庫?
先說一下當(dāng)前Shopee線上在用的幾種數(shù)據(jù)庫:
在Shopee,我們只有兩種關(guān)系數(shù)據(jù)庫:MySQL和TiDB。目前大部分業(yè)務(wù)數(shù)據(jù)運(yùn)行在MySQL上,TiDB集群的比重過去一年來快速增長(zhǎng)中。
Redis在Shopee各個(gè)產(chǎn)品線使用廣泛。從DBA的角度看,Redis是關(guān)系數(shù)據(jù)庫的一種重要補(bǔ)充。
內(nèi)部也在使用諸如HBase和Pika等多種NoSQL數(shù)據(jù)庫,使用范圍多限于特定業(yè)務(wù)和團(tuán)隊(duì)。不在本次討論范圍內(nèi)。
數(shù)據(jù)庫選型策略
過去的一年里,我們明顯感覺到數(shù)據(jù)庫選型在DBA日常工作中的占比越來越重了。隨著業(yè)務(wù)快速成長(zhǎng),DBA每周需要?jiǎng)?chuàng)建的新數(shù)據(jù)庫較之一兩年前可能增加了十倍不止。我們每年都會(huì)統(tǒng)計(jì)幾次線上邏輯數(shù)據(jù)庫個(gè)數(shù)。上圖展示了過去幾年間這個(gè)數(shù)字的增長(zhǎng)趨勢(shì)(縱軸表示邏輯數(shù)據(jù)庫個(gè)數(shù),我們把具體數(shù)字隱去了)。
歷史數(shù)據(jù)顯示,邏輯數(shù)據(jù)庫個(gè)數(shù)每年都有三到五倍的增長(zhǎng),過去的2023年增長(zhǎng)倍數(shù)甚至更高。每個(gè)新數(shù)據(jù)庫上線前,DBA和開發(fā)團(tuán)隊(duì)都需要做一些評(píng)估以快速?zèng)Q定物理設(shè)計(jì)和邏輯設(shè)計(jì)。經(jīng)驗(yàn)表明,一旦在設(shè)計(jì)階段做出了不當(dāng)決定,后期需要付出較多時(shí)間和人力成本來補(bǔ)救。因此,我們需要制定一些簡(jiǎn)潔高效的數(shù)據(jù)庫選型策略,確保我們大多數(shù)時(shí)候都能做出正確選擇。
我們的數(shù)據(jù)庫選型策略可以概括為三點(diǎn):
默認(rèn)使用MySQL。
積極嘗試TiDB。
在必要的時(shí)候引入Redis用于消解部分關(guān)系數(shù)據(jù)庫高并發(fā)讀寫流量。
在使用MySQL的過程中我們發(fā)現(xiàn),當(dāng)單數(shù)據(jù)庫體量達(dá)到TB級(jí)別,開發(fā)和運(yùn)維的復(fù)雜度會(huì)被指數(shù)級(jí)推高。DBA日常工作會(huì)把消除TB級(jí)MySQL數(shù)據(jù)庫實(shí)例排在高優(yōu)先級(jí)。
積極嘗試TiDB”不是一句空話。2023年初開始,我們把TiDB引入了到Shopee。過去兩年間TiDB在Shopee從無到有,集群節(jié)點(diǎn)數(shù)和數(shù)據(jù)體積已經(jīng)達(dá)到了可觀的規(guī)模。對(duì)于一些經(jīng)過了驗(yàn)證的業(yè)務(wù)場(chǎng)景,DBA會(huì)積極推動(dòng)業(yè)務(wù)團(tuán)隊(duì)采用TiDB,讓開發(fā)團(tuán)隊(duì)有機(jī)會(huì)獲得第一手經(jīng)驗(yàn);目前,內(nèi)部多數(shù)業(yè)務(wù)開發(fā)團(tuán)隊(duì)都在線上實(shí)際使用過一次或者多次TiDB。
關(guān)于借助Redis消解關(guān)系數(shù)據(jù)庫高并發(fā)讀寫流量,后面會(huì)展開講一下我們的做法。
分布式數(shù)據(jù)庫選型參考指標(biāo)
在制定了數(shù)據(jù)庫選型策略之后,我們?cè)谶x型中還有幾個(gè)常用的參考指標(biāo):
1TB:對(duì)于一個(gè)新數(shù)據(jù)庫,我們會(huì)問:在未來一年到一年半時(shí)間里,數(shù)據(jù)庫的體積會(huì)不會(huì)漲到1TB?如果開發(fā)團(tuán)隊(duì)很確信新數(shù)據(jù)庫一定會(huì)膨脹到TB級(jí)別,應(yīng)該立即考慮MySQL分庫分表方案或TiDB方案。
1000萬行或10GB:?jiǎn)我籑ySQL表的記錄條數(shù)不要超過1000萬行,或單表磁盤空間占用不要超過10GB。我們發(fā)現(xiàn),超過這個(gè)閾值后,數(shù)據(jù)庫性能和可維護(hù)性上往往也容易出問題(部分SQL難以優(yōu)化,不易做表結(jié)構(gòu)調(diào)整等)。如果確信單表體積會(huì)超越該上限,則應(yīng)考慮MySQL分表方案;也可以采用TiDB,TiDB可實(shí)現(xiàn)水平彈性擴(kuò)展,多數(shù)場(chǎng)景下可免去分表的煩惱。
每秒1000次寫入:?jiǎn)蝹€(gè)MySQL節(jié)點(diǎn)上的寫入速率不要超過每秒1000次。大家可能覺得這個(gè)值太低了;許多開發(fā)同學(xué)也常舉例反駁說,線上MySQL每秒寫入幾千幾萬次的實(shí)際案例比比皆是。我們?yōu)槭裁窗阎笜?biāo)定得如此之低呢?首先,上線前做估算往往有較大不確定性,正常狀況下每秒寫入1000次,大促等特殊場(chǎng)景下可能會(huì)陡然飆升到每秒10000次,作為設(shè)計(jì)指標(biāo)保守一點(diǎn)比較安全。其次,我們?cè)试S開發(fā)團(tuán)隊(duì)在數(shù)據(jù)庫中使用Text等大字段類型,當(dāng)單行記錄長(zhǎng)度增大到一定程度后主庫寫入和從庫復(fù)制性能都可能明顯劣化,這種狀況下對(duì)單節(jié)點(diǎn)寫入速率不宜有太高期待。因此,如果一個(gè)項(xiàng)目上線前就預(yù)計(jì)到每秒寫入速率會(huì)達(dá)到上萬次甚至更高,則應(yīng)該考慮MySQL分庫分表方案或TiDB方案;同時(shí),不妨根據(jù)具體業(yè)務(wù)場(chǎng)景看一下能否引入Redis或消息隊(duì)列作為寫緩沖,實(shí)現(xiàn)數(shù)據(jù)庫寫操作異步化。
P99響應(yīng)時(shí)間要求是1毫秒,10毫秒還是100毫秒?應(yīng)用程序要求99%的數(shù)據(jù)庫查詢都要在1毫秒內(nèi)返回嗎?如果是,則不建議直接讀寫數(shù)據(jù)庫??梢钥紤]引入Redis等內(nèi)存緩沖方案,前端直接面向Redis確保高速讀寫,后端異步寫入數(shù)據(jù)庫實(shí)現(xiàn)數(shù)據(jù)持久化。我們的經(jīng)驗(yàn)是,多數(shù)場(chǎng)景下,MySQL服務(wù)器、表結(jié)構(gòu)設(shè)計(jì)、SQL和程序代碼等方面做過細(xì)致優(yōu)化后,MySQL有望做到99%以上查詢都在10毫秒內(nèi)返回。對(duì)于TiDB,考慮到其存儲(chǔ)計(jì)算分離和多組件協(xié)作實(shí)現(xiàn)SQL執(zhí)行過程的特點(diǎn),我們通常把預(yù)期值調(diào)高一個(gè)數(shù)量級(jí)到100毫秒級(jí)別。以線上某TiDB2.x集群為例,上線半年以來多數(shù)時(shí)候P99都維持在20毫秒以內(nèi),偶爾會(huì)飆升到200毫秒,大促時(shí)抖動(dòng)則更頻繁一些。TiDB執(zhí)行SQL查詢過程中,不同組件、不同節(jié)點(diǎn)之間的交互會(huì)多一些,自然要多花一點(diǎn)時(shí)間。
要不要分庫分表?
內(nèi)部的數(shù)據(jù)庫設(shè)計(jì)評(píng)估清單里包含十幾個(gè)項(xiàng)目,其中要不要分庫分表”是一個(gè)重要議題。在相當(dāng)長(zhǎng)時(shí)間里,MySQL分庫分表方案是我們實(shí)現(xiàn)數(shù)據(jù)庫橫向擴(kuò)展的唯一選項(xiàng);把TiDB引入Shopee后,我們多了一個(gè)不分庫分表”的選項(xiàng)。
從我們的經(jīng)驗(yàn)來看,有幾種場(chǎng)景下采用MySQL分庫分表方案的副作用比較大,日常開發(fā)和運(yùn)維都要付出額外的代價(jià)和成本。DBA和開發(fā)團(tuán)隊(duì)需要在數(shù)據(jù)庫選型階段甄別出這些場(chǎng)景并對(duì)癥下藥。
難以準(zhǔn)確預(yù)估容量的數(shù)據(jù)庫。舉例來講,線上某日志數(shù)據(jù)庫過去三個(gè)月的增量數(shù)據(jù)超過了之前三年多的存量體積。對(duì)于這類數(shù)據(jù)庫,采用分庫分表方案需要一次又一次做Re-sharding,每一次都步驟繁瑣,工程浩大。Shopee的實(shí)踐證明,TiDB是較為理想的日志存儲(chǔ)方案;當(dāng)前,把日志類數(shù)據(jù)存入TiDB已經(jīng)是內(nèi)部較為普遍的做法了。
需要做多維度復(fù)雜查詢的數(shù)據(jù)庫。以訂單數(shù)據(jù)庫為例,各子系統(tǒng)都需要按照買家、賣家、訂單狀態(tài)、支付方式等諸多維度篩選數(shù)據(jù)。若以買家維度分庫分表,則賣家維度的查詢會(huì)變得困難;反之亦然。一方面,我們?yōu)樽钪匾牟樵兙S度分別建立了異構(gòu)索引數(shù)據(jù)庫;另一方面,我們也在TiDB上實(shí)現(xiàn)了訂單匯總表,把散落于各個(gè)分片的訂單數(shù)據(jù)匯入一張TiDB表,讓一些需要掃描全量數(shù)據(jù)的復(fù)雜查詢直接運(yùn)行在TiDB匯總表上。
數(shù)據(jù)傾斜嚴(yán)重的數(shù)據(jù)庫。諸如點(diǎn)贊和關(guān)注等偏社交類業(yè)務(wù)數(shù)據(jù),按照用戶維度分庫分表后常出現(xiàn)數(shù)據(jù)分布不均勻的現(xiàn)象,少數(shù)分片的數(shù)據(jù)量可能遠(yuǎn)大于其他分片;這些大分片往往也是讀寫的熱點(diǎn),進(jìn)而容易成為性能瓶頸。一種常用的解法是Re-sharding,把數(shù)據(jù)分成更多片,盡量稀釋每一片上的數(shù)據(jù)量和讀寫流量。最近我們也開始嘗試把部分?jǐn)?shù)據(jù)搬遷到TiDB上;理論上,如果TiDB表主鍵設(shè)計(jì)得高度分散,熱點(diǎn)數(shù)據(jù)就有望均勻分布到全體TiKVRegion上。
總體來說,MySQL分庫分表方案在解決了主要的數(shù)據(jù)庫橫向擴(kuò)展問題的同時(shí),也導(dǎo)致了一些開發(fā)和運(yùn)維方面的痛點(diǎn)。一方面,我們努力在MySQL分庫分表框架內(nèi)解決和緩解各種問題;另一方面,我們也嘗試基于TiDB構(gòu)建不分庫分表”的新型解決方案,并取得了一些進(jìn)展。
2MySQL在Shopee的使用情況
Shopee的母公司SEAGroup成立于2009年。我們從一開始就使用MySQL作為主力數(shù)據(jù)庫,從早期的MySQL5.1逐漸進(jìn)化到現(xiàn)在的MySQL5.7,我們已經(jīng)用了十年MySQL。
我們使用Percona分支,當(dāng)前存儲(chǔ)引擎以InnoDB為主。
一主多從是比較常見的部署結(jié)構(gòu)。我們的應(yīng)用程序比較依賴讀寫分離,線上數(shù)據(jù)庫可能會(huì)有多達(dá)數(shù)十個(gè)從庫。一套典型的數(shù)據(jù)庫部署結(jié)構(gòu)會(huì)分布在同城多個(gè)機(jī)房;其中會(huì)有至少一個(gè)節(jié)點(diǎn)放在備用機(jī)房,主要用于定時(shí)全量備份,也會(huì)提供給數(shù)據(jù)團(tuán)隊(duì)做數(shù)據(jù)拉取等用途。
如果應(yīng)用程序需要讀取Binlog,從庫上會(huì)安裝一個(gè)名為GDS(GeneralDBSync)的Agent,實(shí)時(shí)解析Binlog,并寫入Kafka。
應(yīng)用程序透過DNS入口連接主庫或從庫。
我們自研的數(shù)據(jù)庫中間件,支持簡(jiǎn)單的分庫分表。何為簡(jiǎn)單的分庫分表”?只支持單一分庫分表規(guī)則,可以按日期、Hash或者某個(gè)字段的取值范圍來分片;一條SQL最終只會(huì)被路由到單一分片上,不支持聚合或Join等操作。
如何解決TB級(jí)MySQL數(shù)據(jù)庫的使用?
根據(jù)我們的統(tǒng)計(jì),Shopee線上數(shù)據(jù)庫中80%都低于50GB;此外,還有2.5%的數(shù)據(jù)庫體積超過1TB。上圖列出了部分TB級(jí)別數(shù)據(jù)庫的一個(gè)統(tǒng)計(jì)結(jié)果:平均體積是2TB,最大的甚至超過4TB。
采用MySQL分庫分表方案和遷移到TiDB是我們削減TB級(jí)MySQL數(shù)據(jù)庫實(shí)例個(gè)數(shù)的兩種主要途徑。除此之外,還有一些辦法能幫助我們對(duì)抗MySQL數(shù)據(jù)庫體積膨脹帶來的負(fù)面效應(yīng)。
舊數(shù)據(jù)歸檔。很多舊數(shù)據(jù)庫占據(jù)了大量磁盤空間,讀寫卻不頻繁。換言之,這些舊數(shù)據(jù)很可能不是『熱數(shù)據(jù)』。如果業(yè)務(wù)上許可,我們通常會(huì)把舊數(shù)據(jù)歸檔到單獨(dú)的MySQL實(shí)例上。當(dāng)然,應(yīng)用程序需要把讀寫這些數(shù)據(jù)的流量改到新實(shí)例。新實(shí)例可以按年或按月把舊數(shù)據(jù)存入不同的表以避免單表體積過大,還可以開啟InnoDB透明頁壓縮以減少磁盤空間占用。TiDB是非常理想的數(shù)據(jù)歸檔選項(xiàng):理論上,一個(gè)TiDB集群的容量可以無限擴(kuò)展,不必?fù)?dān)心磁盤空間不夠用;TiDB在計(jì)算層和存儲(chǔ)層皆可水平彈性擴(kuò)展,我們得以根據(jù)數(shù)據(jù)體積和讀寫流量的實(shí)際增長(zhǎng)循序漸進(jìn)地增加服務(wù)器,使整個(gè)集群的硬件使用效率保持在較為理想的水平。
硬件升級(jí)(Scale-up)。如果MySQL數(shù)據(jù)體積漲到了1TB,磁盤空間開始吃緊,是不是可以先把磁盤空間加倍,內(nèi)存也加大一些,為開發(fā)團(tuán)隊(duì)爭(zhēng)取多一些時(shí)間實(shí)現(xiàn)數(shù)據(jù)庫橫向擴(kuò)展方案?有些數(shù)據(jù)庫體積到了TB級(jí)別,但業(yè)務(wù)上可能不太容易分庫分表。如果開發(fā)團(tuán)隊(duì)能夠通過數(shù)據(jù)歸檔等手段使數(shù)據(jù)體積保持在一個(gè)較為穩(wěn)定(但仍然是TB級(jí)別)的水準(zhǔn),那么適當(dāng)做一下硬件升級(jí)也有助于改善服務(wù)質(zhì)量。
3Redis和關(guān)系型數(shù)據(jù)庫在Shopee的的配合使用
前文中我們提到,使用Redis來解決關(guān)系數(shù)據(jù)庫高并發(fā)讀寫流量的問題,下面我們就來講講具體的做法。
先寫緩存,再寫數(shù)據(jù)庫
比較常用的一種做法是:先寫緩存,再寫數(shù)據(jù)庫。應(yīng)用程序前端直接讀寫Redis,后端勻速異步地把數(shù)據(jù)持久化到MySQL或TiDB。這種做法一般被稱之為穿透式緩存”,其實(shí)是把關(guān)系數(shù)據(jù)庫作為Redis數(shù)據(jù)的持久化存儲(chǔ)層。如果一個(gè)系統(tǒng)在設(shè)計(jì)階段即判明線上會(huì)有較高并發(fā)讀寫流量,把Redis放在數(shù)據(jù)庫前面擋一下往往有效。
在Shopee,一些偏社交類應(yīng)用在大促時(shí)的峰值往往會(huì)比平時(shí)高出數(shù)十上百倍,是典型的性能優(yōu)先型應(yīng)用”(Performance-criticalApplications)。如果開發(fā)團(tuán)隊(duì)事先沒有意識(shí)到這一點(diǎn),按照常規(guī)做法讓程序直接讀寫關(guān)系數(shù)據(jù)庫,大促時(shí)不可避免會(huì)出現(xiàn)一促就倒”的狀況。其實(shí),這類場(chǎng)景很適合借助Redis平緩后端數(shù)據(jù)庫讀寫峰值。
如果Redis集群整體掛掉,怎么辦?一般來說,有兩個(gè)解決辦法:
性能降級(jí):應(yīng)用程序改為直接讀寫數(shù)據(jù)庫。性能上可能會(huì)打一個(gè)大的折扣,但是能保證大部分?jǐn)?shù)據(jù)不丟。一些數(shù)據(jù)較為關(guān)鍵的業(yè)務(wù)可能會(huì)更傾向于采用這種方式。
數(shù)據(jù)降級(jí):切換到一個(gè)空的Redis集群上以盡快恢復(fù)服務(wù)。后續(xù)可以選擇從零開始慢慢積累數(shù)據(jù),或者運(yùn)行另一個(gè)程序從數(shù)據(jù)庫加載部分舊數(shù)據(jù)到Redis。一些并發(fā)高但允許數(shù)據(jù)丟失的業(yè)務(wù)可能會(huì)采用這種方式。
先寫數(shù)據(jù)庫,再寫緩存
還有一種做法也很常見:先寫數(shù)據(jù)庫,再寫緩存。應(yīng)用程序正常讀寫數(shù)據(jù)庫,Shopee內(nèi)部有一個(gè)中間件DEC(DataEventCenter)可以持續(xù)解析Binlog,把結(jié)果重新組織后寫入到Redis。這樣,一部分高頻只讀查詢就可以直接打到Redis上,大幅度降低關(guān)系數(shù)據(jù)庫負(fù)載。
把數(shù)據(jù)寫入Redis的時(shí)候,可以為特定的查詢模式定制數(shù)據(jù)結(jié)構(gòu),一些不太適合用SQL實(shí)現(xiàn)的查詢改為讀Redis之后反而會(huì)更簡(jiǎn)潔高效。
此外,相較于雙寫方式”(業(yè)務(wù)程序同時(shí)把數(shù)據(jù)寫入關(guān)系數(shù)據(jù)庫和Redis),通過解析Binlog的方式在Redis上重建數(shù)據(jù)有明顯好處:業(yè)務(wù)程序?qū)崿F(xiàn)上較為簡(jiǎn)單,不必分心去關(guān)注數(shù)據(jù)庫和Redis之間的數(shù)據(jù)同步邏輯。Binlog方式的缺點(diǎn)在于寫入延遲:新數(shù)據(jù)先寫入MySQL主庫,待其流入到Redis上,中間可能有大約數(shù)十毫秒延遲。實(shí)際使用上要論證業(yè)務(wù)是否能接受這種程度的延遲。
舉例來講,在新訂單實(shí)時(shí)查詢等業(yè)務(wù)場(chǎng)景中,我們常采用這種先寫數(shù)據(jù)庫,再寫緩存”的方式來消解MySQL主庫上的高頻度只讀查詢。為規(guī)避從庫延遲帶來的影響,部分關(guān)鍵訂單字段的查詢須打到MySQL主庫上,大促時(shí)主庫很可能就不堪重負(fù)。歷次大促的實(shí)踐證明,以這種方式引入Redis能有效緩解主庫壓力。
4TiDB在Shopee的使用情況
講完MySQL和Redis,我們來接著講講TiDB。
我們從2023年初開始調(diào)研TiDB,到2023年6月份上線了第一個(gè)TiDB集群(風(fēng)控日志集群,版本1.0.8)。2023年10月份,我們把一個(gè)核心審計(jì)日志庫遷到了TiDB上,目前該集群數(shù)據(jù)量約7TB,日常QPS約為10K~15K??傮w而言,2023年上線的集群以日志類存儲(chǔ)為主。
2023年開始我們嘗試把一些較為核心的線上系統(tǒng)遷移到TiDB上。3月份為買家和賣家提供聊天服務(wù)的Chat系統(tǒng)部分?jǐn)?shù)據(jù)從MySQL遷移到了TiDB。最近的大促中,峰值QPS約為30K,運(yùn)行平穩(wěn)。今年也有一些新功能選擇直接基于TiDB做開發(fā),比如店鋪標(biāo)簽、直播彈幕和選品服務(wù)等。這些新模塊的數(shù)據(jù)量和查詢量都還比較小,有待持續(xù)觀察驗(yàn)證。
TiDB3.0GA后,新的Titan(https://github.com/tikv/titan)存儲(chǔ)引擎吸引了我們。在Shopee,我們?cè)试SMySQL表設(shè)計(jì)中使用Text等大字段類型,通常存儲(chǔ)一些半結(jié)構(gòu)化數(shù)據(jù)。但是,從MySQL遷移到TiDB的過程中,大字段卻可能成為絆腳石。一般而言,TiDB單行數(shù)據(jù)尺寸不宜超過64KB,越小越好;換言之,字段越大,性能越差。Titan存儲(chǔ)引擎有望提高大字段的讀寫性能。目前,我們已經(jīng)著手把一些數(shù)據(jù)遷移到TiKV上,并打開了Titan,希望能探索出更多應(yīng)用場(chǎng)景。
集群概況
目前Shopee線上部署了二十多個(gè)TiDB集群,約有400多個(gè)節(jié)點(diǎn)。版本以TiDB2.1為主,部分集群已經(jīng)開始試水TiDB3.0。我們最大的一個(gè)集群數(shù)據(jù)量約有30TB,超過40個(gè)節(jié)點(diǎn)。到目前為止,用戶、商品和訂單等電商核心子系統(tǒng)都或多或少把一部分?jǐn)?shù)據(jù)和流量放在了TiDB上。
TiDB在Shopee的使用場(chǎng)景
我們把TiDB在Shopee的使用場(chǎng)景歸納為三類:
日志存儲(chǔ)場(chǎng)景
MySQL分庫分表數(shù)據(jù)聚合場(chǎng)景
程序直接讀寫TiDB的場(chǎng)景
第一種使用場(chǎng)景是日志存儲(chǔ)。前面講到過,我們接觸TiDB的第一年里上線的集群以日志類存儲(chǔ)為主。通常的做法是:前端先把日志數(shù)據(jù)寫入到Kafka,后端另一個(gè)程序負(fù)責(zé)把Kafka里的數(shù)據(jù)異步寫入TiDB。由于不用考慮分庫分表,運(yùn)營(yíng)后臺(tái)類業(yè)務(wù)可以方便地讀取TiDB里的日志數(shù)據(jù)。對(duì)于DBA而言,可以根據(jù)需要線性增加存儲(chǔ)節(jié)點(diǎn)和計(jì)算節(jié)點(diǎn),運(yùn)維起來也較MySQL分庫分表簡(jiǎn)單。
第二種使用場(chǎng)景是MySQL分庫分表數(shù)據(jù)聚合。Shopee的訂單表和商品表存在MySQL上,并做了細(xì)致的數(shù)據(jù)分片。為了方便其他子系統(tǒng)讀取訂單和商品數(shù)據(jù),我們做了一層數(shù)據(jù)聚合:借助前面提到的DEC解析MySQLBinlog,把多個(gè)MySQL分片的數(shù)據(jù)聚合到單一TiDB匯總表。這樣,類似BI系統(tǒng)這樣的旁路系統(tǒng)就不必關(guān)注分庫分表規(guī)則,直接讀取TiDB數(shù)據(jù)即可。除此之外,訂單和商品子系統(tǒng)也可以在TiDB匯總表上運(yùn)行一些復(fù)雜的SQL查詢,省去了先在每個(gè)MySQL分片上查一次最后再匯總一次的麻煩。
第三種就是程序直接讀寫TiDB。像前面提到的Chat系統(tǒng),舍棄了MySQL,改為直接讀寫TiDB。優(yōu)勢(shì)體現(xiàn)在兩個(gè)方面:不必做分庫分表,應(yīng)用程序的實(shí)現(xiàn)相對(duì)簡(jiǎn)單、直接;TiDB理論上容量無限大,且方便線性擴(kuò)展,運(yùn)維起來更容易。
前面提到過,在Shopee內(nèi)部使用GDS(GeneralDBSync)實(shí)時(shí)解析MySQLBinlog,并寫入Kafka提供給有需要的客戶端消費(fèi)。TiDB上也可以接一個(gè)Binlog組件,把數(shù)據(jù)變化持續(xù)同步到Kafka上。需要讀取Binlog的應(yīng)用程序只要適配了TiDBBinlog數(shù)據(jù)格式,就可以像消費(fèi)MySQLBinlog一樣消費(fèi)TiDBBinlog了。
從MySQL遷移到TiDB:要適配,不要平移
把數(shù)據(jù)庫從MySQL搬到TiDB的過程中,DBA經(jīng)常提醒開發(fā)同學(xué):要適配,不要平移。關(guān)于這點(diǎn),我們可以舉一個(gè)案例來說明一下。
線上某系統(tǒng)最初采用MySQL分表方案,全量數(shù)據(jù)均分到1000張表;遷移到TiDB后我們?nèi)サ袅朔直恚?000張表合為了一張。應(yīng)用程序上線后,發(fā)現(xiàn)某個(gè)SQL的性能抖動(dòng)比較嚴(yán)重,并發(fā)高的時(shí)候甚至?xí)?dǎo)致整個(gè)TiDB集群卡住。分析后發(fā)現(xiàn)該SQL有兩個(gè)特點(diǎn):
該SQL查詢頻度極高,占了查詢高峰時(shí)全部只讀查詢的90%。
該SQL是一個(gè)較為復(fù)雜的掃表查詢,不易通過添加索引方式優(yōu)化。遷移到TiDB之前,MySQL數(shù)據(jù)庫分為1000張表,該SQL執(zhí)行過程中只會(huì)掃描其中一張表,并且查詢被分散到了多達(dá)二十幾個(gè)從庫上;即便如此,隨著數(shù)據(jù)體積增長(zhǎng),當(dāng)熱數(shù)據(jù)明顯超出內(nèi)存尺寸后,MySQL從庫也變得不堪重負(fù)了。遷移到TiDB并把1000張表合為一張之后,該SQL被迫掃描全量數(shù)據(jù),在TiKV和SQL節(jié)點(diǎn)之間會(huì)有大量中間結(jié)果集傳送流量,性能自然不會(huì)好。
判明原因后,開發(fā)團(tuán)隊(duì)為應(yīng)用程序引入了Redis,把Binlog解析結(jié)果寫入Redis,并針對(duì)上述SQL查詢定制了適當(dāng)?shù)臄?shù)據(jù)結(jié)構(gòu)。這些優(yōu)化措施上線后,90%只讀查詢從TiDB轉(zhuǎn)移到了Redis上,查詢變得更快、更穩(wěn)定;TiDB集群也得以削減數(shù)量可觀的存儲(chǔ)和計(jì)算節(jié)點(diǎn)。
TiDB高度兼容MySQL語法的特點(diǎn)有助于降低數(shù)據(jù)庫遷移的難度;但是,不要忘記它在實(shí)現(xiàn)上完全不同于MySQL,很多時(shí)候我們需要根據(jù)TiDB的特質(zhì)和具體業(yè)務(wù)場(chǎng)景定制出適配的方案。
5總結(jié)
本文回顧了Shopee在關(guān)系數(shù)據(jù)庫選型方面的思路,也附帶簡(jiǎn)單介紹了一些我們?cè)贛ySQL、TiDB和Redis使用方面的心得,希望能為大家提供一點(diǎn)借鑒。
簡(jiǎn)單來說,如果數(shù)據(jù)量比較小,業(yè)務(wù)處于早期探索階段,使用MySQL仍然是一個(gè)很好的選擇。Shopee的經(jīng)驗(yàn)是不用過早的為分庫分表妥協(xié)設(shè)計(jì),因?yàn)楫?dāng)業(yè)務(wù)開始增長(zhǎng),數(shù)據(jù)量開始變大的時(shí)候,可以從MySQL平滑遷移到TiDB,獲得擴(kuò)展性的同時(shí)也不用犧牲業(yè)務(wù)開發(fā)的靈活性。另一方面,Redis可以作為關(guān)系型數(shù)據(jù)庫的很好的補(bǔ)充,用來加速查詢,緩解數(shù)據(jù)庫壓力,使得數(shù)據(jù)庫能夠更關(guān)注吞吐以及強(qiáng)一致場(chǎng)景。
幕思城為您更新最近最有用的電商資訊、電商規(guī)則Shopee,Shopee集群Shopee問答。了解更多電商資訊、行業(yè)動(dòng)向,記得關(guān)注幕思城!
這個(gè)問題還有疑問的話,可以加幕.思.城火星老師免費(fèi)咨詢,微.信號(hào)是為: msc496。