自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

分庫分表很常見,但這些問題90%的人都答不全

數(shù)據(jù)庫 新聞
本文介紹了分庫分表的一些原因,以及如何做分庫分表,并且討論了其中比較關(guān)鍵的分表字段和分表算法的問題。還介紹了幾款比較不錯的分庫分表的相關(guān)框架。

分庫分表,是企業(yè)里面比較常見的針對高并發(fā)、數(shù)據(jù)量大的場景下的一種技術(shù)優(yōu)化方案,也是一個(gè)非常高頻的面試題。但是,因?yàn)楹芏嗳似鋵?shí)并沒有非常豐富的分庫分表的經(jīng)驗(yàn),所以能把這個(gè)問題回答得比較好的人其實(shí)還挺少的。

那么,本文就來試圖把關(guān)于分庫分表的事情,一次性講個(gè)清楚。

一、分庫,分表,分庫分表

首先,我們需要知道所謂"分庫分表",根本就不是一件事兒,而是三件事兒,他們要解決的問題也都不一樣。

這三個(gè)事兒分別是"只分庫不分表"、"只分表不分庫"、以及"既分庫又分表"。

1、什么時(shí)候分庫?

其實(shí),分庫主要解決的是并發(fā)量大的問題。因?yàn)椴l(fā)量一旦上來了,那么數(shù)據(jù)庫就可能會成為瓶頸,因?yàn)閿?shù)據(jù)庫的連接數(shù)是有限的,雖然可以調(diào)整,但是也不是無限調(diào)整的。

所以,當(dāng)當(dāng)你的數(shù)據(jù)庫的讀或者寫的QPS過高,導(dǎo)致你的數(shù)據(jù)庫連接數(shù)不足了的時(shí)候,就需要考慮分庫了,通過增加數(shù)據(jù)庫實(shí)例的方式來提供更多的可用數(shù)據(jù)庫鏈接,從而提升系統(tǒng)的并發(fā)度。

比較典型的分庫的場景就是我們在做微服務(wù)拆分的時(shí)候,就會按照業(yè)務(wù)邊界,把各個(gè)業(yè)務(wù)的數(shù)據(jù)從一個(gè)單一的數(shù)據(jù)庫中拆分開,分表把訂單、物流、商品、會員等單獨(dú)放到單獨(dú)的數(shù)據(jù)庫中。

圖片

還有就是有的時(shí)候可能會需要把歷史訂單挪到歷史庫里面去。這也是分庫的一種具體做法。

2、什么時(shí)候分表?

分庫主要解決的是并發(fā)量大的問題,那分表其實(shí)主要解決的是數(shù)據(jù)量大的問題。

假如你的單表數(shù)據(jù)量非常大,因?yàn)椴l(fā)不高,數(shù)據(jù)量連接可能還夠,但是存儲和查詢的性能遇到了瓶頸了,你做了很多優(yōu)化之后還是無法提升效率的時(shí)候,就需要考慮做分表了。

圖片

通過將數(shù)據(jù)拆分到多張表中,來減少單表的數(shù)據(jù)量,從而提升查詢速度。

一般我們認(rèn)為,單表行數(shù)超過 500 萬行或者單表容量超過 2GB之后,才需要考慮做分庫分表了,小于這個(gè)數(shù)據(jù)量,遇到性能問題先建議大家通過其他優(yōu)化來解決。

3、什么時(shí)候既分庫又分表?

那么什么時(shí)候分庫又分表呢,那就是既需要解決并發(fā)量大的問題,又需要解決數(shù)據(jù)量大的問題時(shí)候。通常情況下,高并發(fā)和數(shù)據(jù)量大的問題都是同時(shí)發(fā)生的,所以,我們會經(jīng)常遇到分庫分表需要同時(shí)進(jìn)行的情況。

所以,當(dāng)你的數(shù)據(jù)庫鏈接也不夠了,并且單表數(shù)據(jù)量也很大導(dǎo)致查詢比較慢的時(shí)候,就需要做既分庫又分表了。

二、橫向拆分和縱向拆分

談及到分庫分表,那就要涉及到該如何做拆分的問題。

通常在做拆分的時(shí)候有兩種分法,分別是橫向拆分(水平拆分)和縱向拆分(垂直拆分)。假如我們有一張表,如果把這張表中某一條記錄的多個(gè)字段,拆分到多張表中,這種就是縱向拆分。那如果把一張表中的不同的記錄分別放到不同的表中,這種就是橫向拆分。

橫向拆分的結(jié)果是數(shù)據(jù)庫表中的數(shù)據(jù)會分散到多張分表中,使得每一個(gè)單表中的數(shù)據(jù)的條數(shù)都有所下降。比如我們可以把不同的用戶的訂單分表拆分放到不同的表中。

圖片

縱向拆分的結(jié)果是數(shù)據(jù)庫表中的數(shù)據(jù)的字段數(shù)會變少,使得每一個(gè)單表中的數(shù)據(jù)的存儲有所下降。比如我可以把商品詳情信息、價(jià)格信息、庫存信息等等分別拆分到不同的表中。

圖片

還有我們談到的針對不同的業(yè)務(wù)做拆分成多個(gè)數(shù)據(jù)庫的這種情況,其實(shí)也是縱向拆分的一種。

三、分表字段的選擇

在分庫分表的過程中,我們需要有一個(gè)字段用來進(jìn)行分表,比如按照用戶分表、按照時(shí)間分表、按照地區(qū)分表。這里面的用戶、時(shí)間、地區(qū)就是所謂的分表字段。

那么,在選擇這個(gè)分表字段的時(shí)候,一定要注意,要根據(jù)實(shí)際的業(yè)務(wù)情況來做慎重的選擇。

比如說我們要對交易訂單進(jìn)行分表的時(shí)候,我們可以選擇的信息有很多,比如買家Id、賣家Id、訂單號、時(shí)間、地區(qū)等等,具體應(yīng)該如何選擇呢?

通常,如果有特殊的訴求,比如按照月度匯總、地區(qū)匯總等以外,我們通常建議大家按照買家Id進(jìn)行分表。因?yàn)檫@樣可以避免一個(gè)關(guān)鍵的問題那就是——數(shù)據(jù)傾斜(熱點(diǎn)數(shù)據(jù))。

1、買家還是賣家?

首先,我們先說為什么不按照賣家分表?

因?yàn)槲覀冎?,電商網(wǎng)站上面是有很多買家和賣家的,但是,一個(gè)大的賣家可能會產(chǎn)生很多訂單,比如像蘇寧易購、當(dāng)當(dāng)?shù)冗@種店鋪,他每天在天貓產(chǎn)生的訂單量就非常的大。如果按照賣家Id分表的話,那同一個(gè)賣家的很多訂單都會分到同一張表。

那就會使得有一些表的數(shù)據(jù)量非常的大,但是有些表的數(shù)據(jù)量又很小,這就是發(fā)生了數(shù)據(jù)傾斜。這個(gè)賣家的數(shù)據(jù)就變成了熱點(diǎn)數(shù)據(jù),隨著時(shí)間的增長,就會使得這個(gè)賣家的所有操作都變得異常緩慢。

圖片

但是,買家ID做分表字段就不會出現(xiàn)這類問題,因?yàn)橐粋€(gè)不太容易出現(xiàn)一個(gè)買家能把數(shù)據(jù)買傾斜了。

但是需要注意的是,我們說按照買家Id做分表,保證的是同一個(gè)買家的所有訂單都在同一張表 ,并不是要給每個(gè)買家都單獨(dú)分配一張表。

我們在做分表路由的時(shí)候,是可以設(shè)定一定的規(guī)則的,比如我們想要分1024張表,那么我們可以用買家ID或者買家ID的hashcode對1024取模,結(jié)果是0000-1023,那么就存儲到對應(yīng)的編號的分表中就行了。

2、賣家查詢怎么辦?

如果按照買家Id進(jìn)行了分表,那賣家的查詢怎么辦,這不就意味著要跨表查詢了嗎?

首先,業(yè)務(wù)問題我們要建立在業(yè)務(wù)背景下討論。電商網(wǎng)站訂單查詢有幾種場景?

  • 買家查自己的訂單
  • 賣家查自己的訂單
  • 平臺的小二查用戶的訂單

首先,我們用買家ID做了分表,那么買家來查詢的時(shí)候,是一定可以把買家ID帶過來的,我們直接去對應(yīng)的表里面查詢就行了。

那如果是賣家查呢?賣家查詢的話,同樣可以帶賣家id過來,那么,我們可以有一個(gè)基于binlog、flink等準(zhǔn)實(shí)時(shí)的同步一張賣家維度的分表,這張表只用來查詢,來解決賣家查詢的問題。

圖片

本質(zhì)上就是用空間換時(shí)間的做法。

不知道大家看到這里會不會有這樣的疑問:同步一張賣家表,這不又帶來了大賣家的熱點(diǎn)問題了嗎?

首先,我們說同步一張賣家維度的表來,但是其實(shí)所有的寫操作還是要寫到買家表的,只不過需要準(zhǔn)實(shí)時(shí)同步的方案同步到賣家表中。也就是說,我們的這個(gè)賣家表理論上是沒有業(yè)務(wù)的寫操作,只有讀操作的。

所以,這個(gè)賣家?guī)熘恍枰懈咝阅艿淖x就行了,那這樣的話就可以有很多選擇了,比如可以部署到一些配置不用那么高的機(jī)器、或者其實(shí)可以干脆就不用MYSQL,而是采用HBASE、PolarDB、Lindorm等數(shù)據(jù)庫就可以了。這些數(shù)據(jù)庫都是可以海量數(shù)據(jù),并提供高性能查詢的。

還有呢就是,大賣家一般都是可以識別的,提前針對大賣家,把他的訂單,再按照一定的規(guī)則拆分到多張表中。因?yàn)橹挥凶x,沒有寫操作,所以拆分多張表也不用考慮事務(wù)的問題。

3、按照訂單查詢怎么辦?

上面說的都是有買賣家ID的情況,那沒有買賣家ID呢?用訂單號直接查怎么辦呢?

這種問題的解決方案是,在生成訂單號的時(shí)候,我們一般會把分表解決編碼到訂單號中去,因?yàn)橛唵紊傻臅r(shí)候是一定可以知道買家ID的,那么我們就把買家ID的路由結(jié)果比如1023,作為一段固定的值放到訂單號中就行了。這就是所謂的"基因法"。

這樣按照訂單號查詢的時(shí)候,解析出這段數(shù)字,直接去對應(yīng)分表查詢就好了。

至于還有人問其他的查詢,沒有買賣家ID,也沒訂單號的,那其實(shí)就屬于是低頻查詢或者非核心功能查詢了,那就可以用ES等搜索引擎的方案來解決了。就不贅述了。

四、分表算法

選定了分表字段之后,如何基于這個(gè)分表字段來準(zhǔn)確的把數(shù)據(jù)分表到某一張表中呢?

這就是分表算法要做的事情了,但是不管什么算法,我們都需要確保一個(gè)前提,那就是同一個(gè)分表字段,經(jīng)過這個(gè)算法處理后,得到的結(jié)果一定是一致的,不可變的。

通常情況下,當(dāng)我們對order表進(jìn)行分表的時(shí)候,比如我們要分成128張表的話,那么得到的128表應(yīng)該是:order_0000、order_0001、order_0002.....order_0126、order_0127。

通常的分表算法有以下幾種:

1、直接取模

在分庫分表時(shí),我們是事先可以知道要分成多少個(gè)庫和多少張表的,所以,比較簡單的就是取模的方式。

比如我們要分成128張表的話,就用一個(gè)整數(shù)來對128取模就行了,得到的結(jié)果如果是0002,那么就把數(shù)據(jù)放到order_0002這張表中。

2、Hash取模

那如果分表字段不是數(shù)字類型,而是字符串類型怎么辦呢?有一個(gè)辦法就是哈希取模,就是先對這個(gè)分表字段取Hash,然后在再取模。

但是需要注意的是,Java中的hash方法得到的結(jié)果有可能是負(fù)數(shù),需要考慮這種負(fù)數(shù)的情況。

3、一致性Hash

前面兩種取模方式都比較不錯,可以使我們的數(shù)據(jù)比較均勻的分布到多張分表中。但是還是存在一個(gè)缺點(diǎn)。

那就是如果需要擴(kuò)容二次分表,表的總數(shù)量發(fā)生變化時(shí),就需要重新計(jì)算hash值,就需要涉及到數(shù)據(jù)遷移了。

為了解決擴(kuò)容的問題,我們可以采用一致性哈希的方式來做分表。

一致性哈??梢园凑粘S玫膆ash算法來將對應(yīng)的key哈希到一個(gè)具有2^32次方個(gè)節(jié)點(diǎn)的空間中,形成成一個(gè)順時(shí)針首尾相接的閉合的環(huán)形。所以當(dāng)添加一臺新的數(shù)據(jù)庫服務(wù)器時(shí),只有增加服務(wù)器的位置和逆時(shí)針方向第一臺服務(wù)器之間的鍵會受影響。

五、全局ID的生成

涉及到分庫分表,就會引申出分布式系統(tǒng)中唯一主鍵ID的生成問題,因?yàn)樵趩伪碇形覀兛梢杂脭?shù)據(jù)庫主鍵來做唯一ID,但是如果做了分庫分表,多張單表中的自增主鍵就一定會發(fā)生沖突。那就不具備全局唯一性了。

那么,如何生成一個(gè)全局唯一的ID呢?有以下幾種方式。

1、UUID

很多人對UUID都不陌生,它是可以做到全局唯一的,而且生成方式也簡單,但是我們通常不推薦使用他做唯一ID,首先UUID太長了,其次字符串的查詢效率也比較慢,而且沒有業(yè)務(wù)含義,根本看不懂。

2、基于某個(gè)單表做自增主鍵

多張單表生成的自增主鍵會沖突,但是如果所有的表中的主鍵都從同一張表生成是不是就可以了。

所有的表在需要主鍵的時(shí)候,都到這張表中獲取一個(gè)自增的ID。

這樣做是可以做到唯一,也能實(shí)現(xiàn)自增,但是問題是這個(gè)單表就變成整個(gè)系統(tǒng)的瓶頸,而且也存在單點(diǎn)問題,一旦他掛了,那整個(gè)數(shù)據(jù)庫就都無法寫入了。

3、基于多個(gè)單表+步長做自增主鍵

為了解決單個(gè)數(shù)據(jù)庫做自曾主鍵的瓶頸及單點(diǎn)故障問題,我們可以引入多個(gè)表來一起生成就行了。

但是如何保證多張表里面生成的Id不重復(fù)呢?如果我們能實(shí)現(xiàn)以下的生成方式就行了:

實(shí)例1生成的ID從1000開始,到1999結(jié)束。

實(shí)例2生成的ID從2000開始,到2999結(jié)束。

實(shí)例3生成的ID從3000開始,到3999結(jié)束。

實(shí)例4生成的ID從4000開始,到4999結(jié)束。

圖片

這樣就能避免ID重復(fù)了,那如果第一個(gè)實(shí)例的ID已經(jīng)用到1999了怎么辦?那就生成一個(gè)新的起始值:

實(shí)例1生成的ID從5000開始,到5999結(jié)束。

實(shí)例2生成的ID從6000開始,到6999結(jié)束。

實(shí)例3生成的ID從7000開始,到7999結(jié)束。

實(shí)例4生成的ID從8000開始,到8999結(jié)束。

我們把步長設(shè)置為1000,確保每一個(gè)單表中的主鍵起始值都不一樣,并且比當(dāng)前的最大值相差1000就行了。

4、雪花算法

雪花算法也是比較常用的一種分布式ID的生成方式,它具有全局唯一、遞增、高可用的特點(diǎn)。

雪花算法生成的主鍵主要由 4 部分組成,1bit符號位、41bit時(shí)間戳位、10bit工作進(jìn)程位以及 12bit 序列號位。

時(shí)間戳占用41bit,精確到毫秒,總共可以容納約69年的時(shí)間。

工作進(jìn)程位占用10bit,其中高位5bit是數(shù)據(jù)中心ID,低位5bit是工作節(jié)點(diǎn)ID,做多可以容納1024個(gè)節(jié)點(diǎn)。

序列號占用12bit,每個(gè)節(jié)點(diǎn)每毫秒0開始不斷累加,最多可以累加到4095,一共可以產(chǎn)生4096個(gè)ID。

所以,一個(gè)雪花算法可以在同一毫秒內(nèi)最多可以生成1024 X 4096 = 4194304個(gè)唯一的ID。

六、分庫分表的工具

在選定了分表字段和分表算法之后,那么,如何把這些功能給實(shí)現(xiàn)出來,需要怎么做呢?

我們?nèi)绾慰梢宰龅较裉幚韱伪硪粯犹幚矸謳旆直淼臄?shù)據(jù)呢?這就需要用到一個(gè)分庫分表的工具了。

目前市面上比較不錯的分庫分表的開源框架主要有三個(gè),分別是sharding-jdbc、TDDL和Mycat。

1、Sharding-JDBC

現(xiàn)在叫ShardingSphere(Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar這3款相互獨(dú)立的產(chǎn)品組成)。它定位為輕量級Java框架,在Java的JDBC層提供的額外服務(wù)。它使用客戶端直連數(shù)據(jù)庫,以jar包形式提供服務(wù),無需額外部署和依賴,可理解為增強(qiáng)版的JDBC驅(qū)動,完全兼容JDBC和各種ORM框架。

開源地址:https://shardingsphere.apache.org

2、TDDL

TDDL 是淘寶開源的一個(gè)用于訪問數(shù)據(jù)庫的中間件, 它集成了分庫分表, 讀寫分離,權(quán)重調(diào)配,動態(tài)數(shù)據(jù)源配置等功能。封裝 jdbc 的 DataSource給用戶提供統(tǒng)一的基于客戶端的使用。

開源地址:https://github.com/alibaba/tb_tddl

3、Mycat

Mycat是一款分布式關(guān)系型數(shù)據(jù)庫中間件。它支持分布式SQL查詢,兼容MySQL通信協(xié)議,以Java生態(tài)支持多種后端數(shù)據(jù)庫,通過數(shù)據(jù)分片提高數(shù)據(jù)查詢處理能力。

開源地址:https://github.com/MyCATApache/Mycat2

七、分庫分表帶來的問題

分庫分表之后,會帶來很多問題。

首先,做了分庫分表之后,所有的讀和寫操作,都需要帶著分表字段,這樣才能知道具體去哪個(gè)庫、哪張表中去查詢數(shù)據(jù)。如果不帶的話,就得支持全表掃描。

但是,單表的時(shí)候全表掃描比較容易,但是做了分庫分表之后,就沒辦法做掃表的操作了,如果要掃表的話就要把所有的物理表都要掃一遍。

還有,一旦我們要從多個(gè)數(shù)據(jù)庫中查詢或者寫入數(shù)據(jù),就有很多事情都不能做了,比如跨庫事務(wù)就是不支持的。

圖片

所以,分庫分表之后就會帶來因?yàn)椴恢С质聞?wù)而導(dǎo)致的數(shù)據(jù)一致性的問題。

其次,做了分庫分表之后,以前單表中很方便的分頁查詢、排序等等操作就都失效了。因?yàn)槲覀儾荒芸缍啾磉M(jìn)行分頁、排序。

總之,分庫分表雖然能解決一些大數(shù)據(jù)量、高并發(fā)的問題,但是同時(shí)也會帶來一些新的問題。所以,在做數(shù)據(jù)庫優(yōu)化的時(shí)候,還是建議大家優(yōu)先選擇其他的優(yōu)化方式,最后再考慮分庫分表。

八、總結(jié)

以上,本文介紹了分庫分表的一些原因,以及如何做分庫分表,并且討論了其中比較關(guān)鍵的分表字段和分表算法的問題。還介紹了幾款比較不錯的分庫分表的相關(guān)框架。

最后,還有一些需要大家注意的就是分庫分表會引入一些新的問題,這些問題的解決成本也都不低,所以在做技術(shù)選型的時(shí)候也要做好這方面的評估。

責(zé)任編輯:張燕妮 來源: dbaplus社群
相關(guān)推薦

2024-02-21 12:17:00

2025-04-03 07:41:55

API阻塞隊(duì)列數(shù)據(jù)

2023-03-10 18:20:07

客戶端開源中間件

2024-09-12 11:51:44

2022-12-09 09:21:10

分庫分表算法

2019-07-31 09:27:23

數(shù)據(jù)庫MySQLSQL

2020-07-30 17:59:34

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

2019-11-12 09:54:20

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

2022-05-25 08:06:37

MySQL分庫分表

2021-03-15 08:02:43

線性結(jié)構(gòu)PriorityQue

2021-08-31 20:21:11

VitessMySQL分庫

2023-08-11 08:59:49

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

2020-11-18 09:39:02

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

2022-07-11 08:16:47

NewSQL關(guān)系數(shù)據(jù)庫系統(tǒng)

2019-09-17 09:31:10

2020-07-28 09:04:09

NewSQL分庫分表

2021-01-26 05:37:08

分庫分表內(nèi)存

2024-07-26 00:16:11

2025-04-01 08:45:00

2022-09-16 11:41:17

Spring代碼
點(diǎn)贊
收藏

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