浅谈分库分表那些事儿
本文適合閱讀群眾:需要從單庫(kù)單表改造為多庫(kù)多表的新手。
本文主要闡述在分庫(kù)分表改造過(guò)程中需要考慮的因素以及對(duì)應(yīng)的解法,還有踩過(guò)的那些坑。
一 前言
我們既然要做分庫(kù)分表,那總要有個(gè)做事的動(dòng)機(jī)。那么,在動(dòng)手之前,首先就要弄明白下面兩個(gè)問(wèn)題。
1 什么是分庫(kù)分表?
其實(shí)就是字面意思,很好理解:
- 分庫(kù):從單個(gè)數(shù)據(jù)庫(kù)拆分成多個(gè)數(shù)據(jù)庫(kù)的過(guò)程,將數(shù)據(jù)散落在多個(gè)數(shù)據(jù)庫(kù)中。
- 分表:從單張表拆分成多張表的過(guò)程,將數(shù)據(jù)散落在多張表內(nèi)。
2 為什么要分庫(kù)分表?
關(guān)鍵字:提升性能、增加可用性。
從性能上看
隨著單庫(kù)中的數(shù)據(jù)量越來(lái)越大、數(shù)據(jù)庫(kù)的查詢QPS越來(lái)越高,相應(yīng)的,對(duì)數(shù)據(jù)庫(kù)的讀寫(xiě)所需要的時(shí)間也越來(lái)越多。數(shù)據(jù)庫(kù)的讀寫(xiě)性能可能會(huì)成為業(yè)務(wù)發(fā)展的瓶頸。對(duì)應(yīng)的,就需要做數(shù)據(jù)庫(kù)性能方面的優(yōu)化。本文中我們只討論數(shù)據(jù)庫(kù)層面的優(yōu)化,不討論緩存等應(yīng)用層優(yōu)化的手段。
如果數(shù)據(jù)庫(kù)的查詢QPS過(guò)高,就需要考慮拆庫(kù),通過(guò)分庫(kù)來(lái)分擔(dān)單個(gè)數(shù)據(jù)庫(kù)的連接壓力。比如,如果查詢QPS為3500,假設(shè)單庫(kù)可以支撐1000個(gè)連接數(shù)的話,那么就可以考慮拆分成4個(gè)庫(kù),來(lái)分散查詢連接壓力。
如果單表數(shù)據(jù)量過(guò)大,當(dāng)數(shù)據(jù)量超過(guò)一定量級(jí)后,無(wú)論是對(duì)于數(shù)據(jù)查詢還是數(shù)據(jù)更新,在經(jīng)過(guò)索引優(yōu)化等純數(shù)據(jù)庫(kù)層面的傳統(tǒng)優(yōu)化手段之后,還是可能存在性能問(wèn)題。這是量變產(chǎn)生了質(zhì)變,這時(shí)候就需要去換個(gè)思路來(lái)解決問(wèn)題,比如:從數(shù)據(jù)生產(chǎn)源頭、數(shù)據(jù)處理源頭來(lái)解決問(wèn)題,既然數(shù)據(jù)量很大,那我們就來(lái)個(gè)分而治之,化整為零。這就產(chǎn)生了分表,把數(shù)據(jù)按照一定的規(guī)則拆分成多張表,來(lái)解決單表環(huán)境下無(wú)法解決的存取性能問(wèn)題。
從可用性上看
單個(gè)數(shù)據(jù)庫(kù)如果發(fā)生意外,很可能會(huì)丟失所有數(shù)據(jù)。尤其是云時(shí)代,很多數(shù)據(jù)庫(kù)都跑在虛擬機(jī)上,如果虛擬機(jī)/宿主機(jī)發(fā)生意外,則可能造成無(wú)法挽回的損失。因此,除了傳統(tǒng)的 Master-Slave、Master-Master 等部署層面解決可靠性問(wèn)題外,我們也可以考慮從數(shù)據(jù)拆分層面解決此問(wèn)題。
此處我們以數(shù)據(jù)庫(kù)宕機(jī)為例:
- 單庫(kù)部署情況下,如果數(shù)據(jù)庫(kù)宕機(jī),那么故障影響就是100%,而且恢復(fù)可能耗時(shí)很長(zhǎng)。
- 如果我們拆分成2個(gè)庫(kù),分別部署在不同的機(jī)器上,此時(shí)其中1個(gè)庫(kù)宕機(jī),那么故障影響就是50%,還有50%的數(shù)據(jù)可以繼續(xù)服務(wù)。
- 如果我們拆分成4個(gè)庫(kù),分別部署在不同的機(jī)器上,此時(shí)其中1個(gè)庫(kù)宕機(jī),那么故障影響就是25%,還有75%的數(shù)據(jù)可以繼續(xù)服務(wù),恢復(fù)耗時(shí)也會(huì)很短。
當(dāng)然,我們也不能無(wú)限制的拆庫(kù),這也是犧牲存儲(chǔ)資源來(lái)提升性能、可用性的方式,畢竟資源總是有限的。
二 如何分庫(kù)分表
1 分庫(kù)?分表?還是既分庫(kù)又分表?
從第一部分了解到的信息來(lái)看,分庫(kù)分表方案可以分為下面3種:
2 如何選擇我們自己的切分方案?
如果需要分表,那么分多少?gòu)埍砗线m?
由于所有的技術(shù)都是為業(yè)務(wù)服務(wù)的,那么,我們就先從數(shù)據(jù)方面回顧下業(yè)務(wù)背景。
比如,我們這個(gè)業(yè)務(wù)系統(tǒng)是為了解決會(huì)員的咨詢?cè)V求,通過(guò)我們的XSpace客服平臺(tái)系統(tǒng)來(lái)服務(wù)會(huì)員,目前主要以同步的離線工單數(shù)據(jù)作為我們的數(shù)據(jù)源來(lái)構(gòu)建自己的數(shù)據(jù)。
假設(shè),每一筆離線工單都會(huì)產(chǎn)生對(duì)應(yīng)一筆會(huì)員的咨詢問(wèn)題(我們簡(jiǎn)稱:問(wèn)題單),如果:
- 在線渠道:每天產(chǎn)生 3w 筆聊天會(huì)話,假設(shè),其中50%的會(huì)話會(huì)生成一筆離線工單,那么每天可生成 3w * 50% = 1.5w 筆工單;
- 熱線渠道:每天產(chǎn)生 2.5w 通電話,假設(shè),其中80%的電話都會(huì)產(chǎn)生一筆工單,那么每天可生成 2.5w * 80% = 2w 筆/天;
- 離線渠道:假設(shè)離線渠道每天直接生成 3w 筆;
合計(jì)共 1.5w + 2w + 3w = 6.5w 筆/天
考慮到以后可能要繼續(xù)覆蓋的新的業(yè)務(wù)場(chǎng)景,需要提前預(yù)留部分?jǐn)U展空間,這里我們假設(shè)為每天產(chǎn)生 8w 筆問(wèn)題單。
除問(wèn)題單外,還有另外2張常用的業(yè)務(wù)表:用戶操作日志表、用戶提交的表單數(shù)據(jù)表。
其中,每筆問(wèn)題單都會(huì)產(chǎn)生多條用戶操作日志,根據(jù)歷史統(tǒng)計(jì)數(shù)據(jù)來(lái)可以看到,平均每個(gè)問(wèn)題單大約會(huì)產(chǎn)生8條操作日志,我們預(yù)留一部分空間,假設(shè)每個(gè)問(wèn)題單平均產(chǎn)生約10條用戶操作日志。
如果系統(tǒng)設(shè)計(jì)使用年限5年,那么問(wèn)題單數(shù)據(jù)量大約 = 5年 365天/年 8w/天 = 1.46億,那么估算出的表數(shù)量如下:
- 問(wèn)題單需要:1.46億/500w = 29.2 張表,我們就按 32 張表來(lái)切分;
- 操作日志需要 :32 10 = 320 張表,我們就按 32 16 = 512 張表來(lái)切分。
如果需要分庫(kù),那么分多少庫(kù)合適?
分庫(kù)的時(shí)候除了要考慮平時(shí)的業(yè)務(wù)峰值讀寫(xiě)QPS外,還要考慮到諸如雙11大促期間可能達(dá)到的峰值,需要提前做好預(yù)估。
根據(jù)我們的實(shí)際業(yè)務(wù)場(chǎng)景,問(wèn)題單的數(shù)據(jù)查詢來(lái)源主要來(lái)自于阿里客服小蜜首頁(yè)。因此,可以根據(jù)歷史QPS、RT等數(shù)據(jù)評(píng)估,假設(shè)我們只需要3500數(shù)據(jù)庫(kù)連接數(shù),如果單庫(kù)可以承擔(dān)最高1000個(gè)數(shù)據(jù)庫(kù)連接,那么我們就可以拆分成4個(gè)庫(kù)。
3 如何對(duì)數(shù)據(jù)進(jìn)行切分?
根據(jù)行業(yè)慣例,通常按照 水平切分、垂直切分 兩種方式進(jìn)行切分,當(dāng)然,有些復(fù)雜業(yè)務(wù)場(chǎng)景也可能選擇兩者結(jié)合的方式。
(1)水平切分
這是一種橫向按業(yè)務(wù)維度切分的方式,比如常見(jiàn)的按會(huì)員維度切分,根據(jù)一定的規(guī)則把不同的會(huì)員相關(guān)的數(shù)據(jù)散落在不同的庫(kù)表中。由于我們的業(yè)務(wù)場(chǎng)景決定都是從會(huì)員視角進(jìn)行數(shù)據(jù)讀寫(xiě),所以,我們就選擇按照水平方式進(jìn)行數(shù)據(jù)庫(kù)切分。
(2)垂直切分
垂直切分可以簡(jiǎn)單理解為,把一張表的不同字段拆分到不同的表中。
比如:假設(shè)有個(gè)小型電商業(yè)務(wù),把一個(gè)訂單相關(guān)的商品信息、買(mǎi)賣家信息、支付信息都放在一張大表里。可以考慮通過(guò)垂直切分的方式,把商品信息、買(mǎi)家信息、賣家信息、支付信息都單獨(dú)拆分成獨(dú)立的表,并通過(guò)訂單號(hào)跟訂單基本信息關(guān)聯(lián)起來(lái)。
也有一種情況,如果一張表有10個(gè)字段,其中只有3個(gè)字段需要頻繁修改,那么就可以考慮把這3個(gè)字段拆分到子表。避免在修改這3個(gè)數(shù)據(jù)時(shí),影響到其余7個(gè)字段的查詢行鎖定。
三 分庫(kù)分表之后帶來(lái)的新問(wèn)題
1 分庫(kù)分表后,如何讓數(shù)據(jù)均勻散落在各個(gè)分庫(kù)分表內(nèi)?
比如,當(dāng)熱點(diǎn)事件出現(xiàn)后,怎么避免熱點(diǎn)數(shù)據(jù)集中存取到某個(gè)特定庫(kù)/表,造成各分庫(kù)分表讀寫(xiě)壓力不均的問(wèn)題。
其實(shí),細(xì)思之下可以發(fā)現(xiàn)這個(gè)問(wèn)題其實(shí)跟負(fù)載均衡的問(wèn)題很相似,所以,我們可以去借鑒下負(fù)載均衡的解法來(lái)解決。我們常見(jiàn)的負(fù)責(zé)均衡算法如下:
我們的選擇:基于 一致性Hash算法 裁剪,相較于一致性Hash算法,我們裁剪后的算法
主要區(qū)別在以下幾個(gè)點(diǎn):
(1)Hash環(huán)節(jié)點(diǎn)數(shù)量的不同
一致性Hash有2^32-1個(gè)節(jié)點(diǎn),考慮到我們按照buyerId切分,而且buyerId基數(shù)本就很龐大,整體已經(jīng)具備一定的均勻度,所以,我們把Hash環(huán)的數(shù)量降低到4096個(gè);
(2)DB索引算法的不同
一致性Hash通過(guò)類似 hash(DB的IP) % 2^32 公式計(jì)算DB在Hash環(huán)的位置。如果DB數(shù)量較少,需要通過(guò)增加虛擬節(jié)點(diǎn)來(lái)解決Hash環(huán)偏斜問(wèn)題,而且DB的位置可能會(huì)隨著IP的變動(dòng)而變化,尤其是在云環(huán)境下。
數(shù)據(jù)均勻分布到Hash環(huán)的問(wèn)題,經(jīng)過(guò)之前的判斷,我們可以通過(guò) Math.abs(buyerId.hashCode()) % 4096 計(jì)算定位到Hash環(huán)位置,那么剩下的問(wèn)題就是讓DB也均勻分布到這個(gè)Hash環(huán)上即可。由于我們都是使用阿里的TDDL中間件,只需要通過(guò)邏輯上的分庫(kù)索引號(hào)定位DB,因此,我們把分庫(kù)DB均分到這個(gè)Hash環(huán)上即可,如果是hash環(huán)有4096個(gè)環(huán)節(jié),拆分4庫(kù)的話,那么4個(gè)庫(kù)分別位于第1、1025、2049、3073個(gè)節(jié)點(diǎn)上。分庫(kù)的索引定位可通過(guò) (Math.abs(buyerId.hashCode()) % 4096) / (4096 / DB_COUNT) 這個(gè)公式計(jì)算得出。
分庫(kù)索引的 Java 偽代碼實(shí)現(xiàn)如下:
/*** 分庫(kù)數(shù)量*/ public static final int DB_COUNT = 4;/*** 獲取數(shù)據(jù)庫(kù)分庫(kù)索引號(hào)** @param buyerId 會(huì)員ID* @return*/ public static int indexDbByBuyerId(Long buyerId) {return (Math.abs(buyerId.hashCode()) % 4096) / (4096 / DB_COUNT); }2 分庫(kù)分表環(huán)境下,如何解決分庫(kù)后主鍵ID的唯一性問(wèn)題?
在單庫(kù)環(huán)境下,我們的問(wèn)題單主表的ID采用的MySQL自增的方式。但是,分庫(kù)之后如果還繼續(xù)使用數(shù)據(jù)庫(kù)自增的方式,就很容易出現(xiàn)各門(mén)口的主鍵ID重復(fù)問(wèn)題。
對(duì)于這種情況,有很多種解決方案,比如采用UUID的方式,不過(guò)UUID太長(zhǎng),查詢性能太差,占用空間也大,而且主鍵的類型也變了,也不利于應(yīng)用平滑遷移。
其實(shí),我們也可以對(duì)ID繼續(xù)拆分,比如對(duì)ID進(jìn)行分段,不同的庫(kù)表使用不同的ID段,但也會(huì)產(chǎn)生新的問(wèn)題,這個(gè)ID段要多長(zhǎng)才合適?如果ID段分配完了,那可能會(huì)占用第二個(gè)庫(kù)的ID段,產(chǎn)生ID不唯一問(wèn)題。
但是,如果我們讓所有的分庫(kù)使用的ID段按照等差數(shù)列進(jìn)行分隔,每次ID段用完之后,再按照固定的步長(zhǎng)比遞增的話,那是不是就可以解決這個(gè)問(wèn)題了。
比如,像下面這樣,假設(shè)每次分配的ID間隔為1000,也就是步長(zhǎng)1000,那么每次分配的ID段起止索引則可以按照下面的公式計(jì)算得出:
- 第X庫(kù)、第Y次分配的ID段起始索引就是:
- 第X庫(kù)、第Y次分配的ID段結(jié)束索引就是:
如果是分4庫(kù),那么最終分配的ID段就會(huì)是下面這個(gè)樣子:
我們的問(wèn)題單庫(kù)采用的就是這種先對(duì)ID分段,再按固定步長(zhǎng)遞增的方式。這也是TDDL官方提供的解決方案。
除此之外,實(shí)際場(chǎng)景下,通常為了分析排查問(wèn)題方便,往往會(huì)在ID中增加一些額外信息,比如我們自己的問(wèn)題單ID就包含了日期、版本、分庫(kù)索引等信息。
問(wèn)題單 ID 生成 Java 偽代碼參考:
import lombok.Setter; import org.apache.commons.lang3.time.DateFormatUtils;/*** 問(wèn)題單ID構(gòu)建器* <p>* ID格式(18位):6位日期 + 2位版本號(hào) + 2位庫(kù)索引號(hào) + 8位序列號(hào)* 示例:180903010300001111* 說(shuō)明這個(gè)問(wèn)題單是2018年9月3號(hào)生成的,采用的01版本的ID生成規(guī)則,數(shù)據(jù)存放在03庫(kù),最后8位00001111是生成的序列號(hào)ID。* 采用這種ID格式還有個(gè)好處就是每天都有1億(8位)的序列號(hào)可用。* </p>*/ @Setter public class ProblemOrdIdBuilder {public static final int DB_COUNT = 4; private static final String DATE_FORMATTER = "yyMMdd";private String version = "01";private long buyerId;private long timeInMills;private long seqNum;public Long build() {int dbIndex = indexDbByBuyerId(buyerId);StringBuilder pid = new StringBuilder(18).append(DateFormatUtils.format(timeInMills, DATE_FORMATTER)).append(version).append(String.format("%02d", dbIndex)).append(String.format("%08d", seqNum % 10000000));return Long.valueOf(pid.toString());}/*** 獲取數(shù)據(jù)庫(kù)分庫(kù)索引號(hào)** @param buyerId 會(huì)員ID* @return*/public int indexDbByBuyerId(Long buyerId) {return (Math.abs(buyerId.hashCode()) % 4096) / (4096 / DB_COUNT);} }3 分庫(kù)分表環(huán)境下,事務(wù)問(wèn)題怎么解決?
由于分布式環(huán)境下,一個(gè)事務(wù)可能跨多個(gè)分庫(kù),所以,處理起來(lái)相對(duì)復(fù)雜。目前常見(jiàn)的有2種解決方案:
(1)使用分布式事務(wù)
- 優(yōu)點(diǎn):由應(yīng)用服務(wù)器/數(shù)據(jù)庫(kù)去管理事務(wù),實(shí)現(xiàn)簡(jiǎn)單
- 缺點(diǎn):性能代價(jià)較高,尤其是涉及到分庫(kù)數(shù)量較多時(shí)尤為明顯。而且,還依賴于一些特定的應(yīng)用服務(wù)器/數(shù)據(jù)庫(kù)提供的分布式事務(wù)實(shí)現(xiàn)方案。
(2)由應(yīng)用程序+數(shù)據(jù)庫(kù)共同控制
- 原理:大事化小,將多個(gè)大事務(wù)拆分成可由單個(gè)分庫(kù)處理的小事務(wù),由應(yīng)用程序去控制這些小事務(wù)。
- 優(yōu)點(diǎn):性能良好,少了一個(gè)分布式事務(wù)協(xié)調(diào)處理層
- 缺點(diǎn):需要從應(yīng)用程序自身上做事務(wù)控制的靈活設(shè)計(jì)。從業(yè)務(wù)應(yīng)用上做處理,應(yīng)該改造成本高。
針對(duì)上面2種分布式事務(wù)解決方案,我們?cè)撊绾芜x擇?
首先,沒(méi)有萬(wàn)能的解決方案,只有適合自己的方案。那就先看看我們的業(yè)務(wù)中,事務(wù)的使用場(chǎng)景有哪些吧。
無(wú)論是來(lái)咨詢問(wèn)題的會(huì)員,還是為會(huì)員解決問(wèn)題的客服小二,亦或者從第三方系統(tǒng)同步相關(guān)數(shù)據(jù)。主要有2個(gè)核心動(dòng)作:
- 以會(huì)員維度查詢相關(guān)進(jìn)度數(shù)據(jù),包含會(huì)員問(wèn)題數(shù)據(jù),以及對(duì)應(yīng)的問(wèn)題處理操作日志/進(jìn)度數(shù)據(jù);
- 以會(huì)員視角提交相關(guān)憑證/反饋新情況等數(shù)據(jù),或者是客服小二代會(huì)員提交這些數(shù)據(jù)。提交的數(shù)據(jù)也可能會(huì)決定問(wèn)題是否解決(被完結(jié))。
由于問(wèn)題單數(shù)據(jù)、操作日志都是分開(kāi)查詢,所以,不涉及分布式關(guān)聯(lián)查詢場(chǎng)景,這個(gè)可以忽略不考慮。
那么就剩下用戶提交數(shù)據(jù)場(chǎng)景了,可能會(huì)同時(shí)寫(xiě)入問(wèn)題單以及操作日志數(shù)據(jù)。
既然使用場(chǎng)景確定了,那么可以選擇事務(wù)解決方案了。雖然分布式事務(wù)實(shí)現(xiàn)簡(jiǎn)單,但這個(gè)簡(jiǎn)單是因?yàn)橹虚g件幫我們解決了它本身的復(fù)雜性。復(fù)雜性越高,必然會(huì)帶來(lái)一定的性能損耗。而且,目前大部分應(yīng)用都是基于 SpringBoot 開(kāi)發(fā),默認(rèn)使用的都是內(nèi)嵌 tomcat 容器,不像 IBM 提供的 WebSphere Application Server、Oracle 的 WebLogic 這些重量級(jí)應(yīng)用服務(wù)器,都提供了內(nèi)置的分布式事務(wù)管理器。因此,如果我們要接入,必然要自己引入額外的分布式事務(wù)管理器,這個(gè)接入成本就更高了。所以,這種方案就暫不考慮了。那么,就只能自己想辦法把大事務(wù)切分成單庫(kù)可以解決的小事務(wù)了。
所以,現(xiàn)在問(wèn)題就成了,如何讓同一個(gè)會(huì)員的問(wèn)題單數(shù)據(jù)和這個(gè)問(wèn)題單相關(guān)的操作日志數(shù)據(jù)寫(xiě)入到同一個(gè)分庫(kù)中。其實(shí),解決方案也比較簡(jiǎn)單,由于都是使用會(huì)員ID做切分,那么使用相同的分庫(kù)路由規(guī)則即可。
最后,我來(lái)看下最終的 TDDL 分庫(kù)分表規(guī)則配置:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"><beans><bean id="vtabroot" class="com.taobao.tddl.interact.rule.VirtualTableRoot" init-method="init"><property name="dbType" value="MYSQL" /><property name="defaultDbIndex" value="PROBLEM_0000_GROUP" /><property name="tableRules"><map><entry key="problem_ord" value-ref="problem_ord" /><entry key="problem_operate_log" value-ref="problem_operate_log" /></map></property></bean><!-- 問(wèn)題(訴求)單表 --><bean id="problem_ord" class="com.taobao.tddl.interact.rule.TableRule"><property name="dbNamePattern" value="PROBLEM_{0000}_GROUP" /><property name="tbNamePattern" value="problem_ord_{0000}" /><property name="dbRuleArray" value="((Math.abs(#buyer_id,1,4#.hashCode()) % 4096).intdiv(1024))" /><property name="tbRuleArray"><list><value><![CDATA[def hashCode = Math.abs(#buyer_id,1,32#.hashCode());int dbIndex = ((hashCode % 4096).intdiv(1024)) as int;int tableCountPerDb = 32 / 4;int tableIndexStart = dbIndex * tableCountPerDb;int tableIndexOffset = (hashCode % tableCountPerDb) as int;int tableIndex = tableIndexStart + tableIndexOffset;return tableIndex;]]></value></list></property><property name="allowFullTableScan" value="false" /></bean><!-- 問(wèn)題操作日志表 --><bean id="problem_operate_log" class="com.taobao.tddl.interact.rule.TableRule"><property name="dbNamePattern" value="PROBLEM_{0000}_GROUP" /><property name="tbNamePattern" value="problem_operate_log_{0000}" /><!-- 【#buyer_id,1,4#.hashCode()】 --><!-- buyer_id 代表分片字段;1代表分庫(kù)步長(zhǎng);4代表一共4個(gè)分庫(kù),當(dāng)執(zhí)行全表掃描時(shí)會(huì)用到 --><property name="dbRuleArray" value="((Math.abs(#buyer_id,1,4#.hashCode()) % 4096).intdiv(1024))" /><property name="tbRuleArray"><list><value><![CDATA[def hashCode = Math.abs(#buyer_id,1,512#.hashCode());int dbIndex = ((hashCode % 4096).intdiv(1024)) as int;int tableCountPerDb = 512 / 4;int tableIndexStart = dbIndex * tableCountPerDb;int tableIndexOffset = (hashCode % tableCountPerDb) as int;int tableIndex = tableIndexStart + tableIndexOffset;return tableIndex;]]></value></list></property><property name="allowFullTableScan" value="false" /></bean> </beans>4 分庫(kù)分表后,歷史數(shù)據(jù)如何平滑遷移?
數(shù)據(jù)庫(kù)復(fù)制方案,阿里云上面也開(kāi)放了以前阿里內(nèi)部使用的數(shù)據(jù)庫(kù)復(fù)制、遷移方案《數(shù)據(jù)傳輸服務(wù)(Data Transmission Service)》[1],詳情可咨詢阿里云客服或者阿里云數(shù)據(jù)庫(kù)專家。
分庫(kù)切換發(fā)布流程可選擇停機(jī)、不停機(jī)發(fā)布兩種:
(1)如果選擇停機(jī)發(fā)布
- 首先,要選擇一個(gè)夜黑風(fēng)高、四處無(wú)人的夜晚。寒風(fēng)刺骨能讓你清醒,四處無(wú)人,你好辦事打劫偷數(shù)據(jù),我們就挑了個(gè)凌晨4點(diǎn)寂靜無(wú)人的時(shí)候做切換;如果可以,能臨時(shí)關(guān)閉業(yè)務(wù)訪問(wèn)入口最好。
- 然后,在DTS上面新增一個(gè)全量的數(shù)據(jù)復(fù)制任務(wù),把單庫(kù)的數(shù)據(jù)復(fù)制到新的分庫(kù)中(這個(gè)過(guò)程很快,千萬(wàn)級(jí)數(shù)據(jù)應(yīng)該10分左右就能搞定);
- 之后,切換 TDDL 配置(單庫(kù)->分庫(kù)),并重啟應(yīng)用,檢查是否生效。
- 最后,開(kāi)放業(yè)務(wù)訪問(wèn)入口,提供服務(wù)。
(2)如果選擇不停機(jī)發(fā)布話,流程會(huì)略微復(fù)雜點(diǎn)
- 首先,同樣需要選擇一個(gè)夜黑風(fēng)高的夜晚,來(lái)襯托你的帥氣。
- 然后,通過(guò)DTS復(fù)制某個(gè)時(shí)間點(diǎn)前的數(shù)據(jù),比如:今天前的歷史數(shù)據(jù)。
- 之后,從單庫(kù)切換到分庫(kù)(最好是提前發(fā)布好應(yīng)用、準(zhǔn)備好配置),這樣切換時(shí)只需要幾分鐘重啟生效即可。在切換到分庫(kù)前,聯(lián)系DBA在切換期間停止老的單庫(kù)讀寫(xiě)。
- 最后,分庫(kù)切換完成后,再通過(guò)DTS增量復(fù)制老的單庫(kù)中今天凌晨之后產(chǎn)生的數(shù)據(jù)。
- 最后的最后,持續(xù)觀察一段時(shí)間,如果沒(méi)問(wèn)題,老的單庫(kù)就可以下線了。
5 TDDL配置分庫(kù)分表路由時(shí)的注意事項(xiàng)
由于阿里的TDDL中間件使用groovy腳本計(jì)算分庫(kù)分表路由,而 groovy 的 / 運(yùn)算符 或者 /= 運(yùn)算符 可能會(huì)產(chǎn)生一個(gè) double 類型的結(jié)果,并非像 Java 那樣得出一個(gè)整數(shù),因此需要使用 x.intdiv(y) 函數(shù)做整除運(yùn)算。
// 在 Java 中 System.out.println(5 / 3); // 結(jié)果 = 1// 在 Groovy 中 println (5 / 3); // 結(jié)果 = 1.6666666667 println (5.intdiv(3)); // 結(jié)果 = 1(Groovy整除正確用法)詳情可查看 Groovy 官方說(shuō)明 《The case of the division operator》:
四 分庫(kù)分表文中案例圖示
參考資料[1]https://baijiahao.baidu.com/s?id=1622441635115622194&wfr=spider&for=pc
[2]http://www.zsythink.net/archives/1182
[3]https://www.aliyun.com/product/dts
[4]https://docs.groovy-lang.org/latest/html/documentation/core-syntax.html#integer_division
[5]https://github.com/alibaba/tb_tddl
原文鏈接:https://developer.aliyun.com/article/782489?
版權(quán)聲明:本文內(nèi)容由阿里云實(shí)名注冊(cè)用戶自發(fā)貢獻(xiàn),版權(quán)歸原作者所有,阿里云開(kāi)發(fā)者社區(qū)不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。具體規(guī)則請(qǐng)查看《阿里云開(kāi)發(fā)者社區(qū)用戶服務(wù)協(xié)議》和《阿里云開(kāi)發(fā)者社區(qū)知識(shí)產(chǎn)權(quán)保護(hù)指引》。如果您發(fā)現(xiàn)本社區(qū)中有涉嫌抄襲的內(nèi)容,填寫(xiě)侵權(quán)投訴表單進(jìn)行舉報(bào),一經(jīng)查實(shí),本社區(qū)將立刻刪除涉嫌侵權(quán)內(nèi)容。總結(jié)
以上是生活随笔為你收集整理的浅谈分库分表那些事儿的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 函数计算镜像加速:从分钟到秒的跨越
- 下一篇: 揭秘更加开放的数据库服务:阿里云数据库专