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

分庫分表核心理念,你學(xué)會了嗎?

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

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

這三件事分別是"只分庫不分表"、"只分表不分庫"、以及"既分庫又分表"。

什么時候分庫?

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

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

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

圖片圖片

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

什么時候分表?

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

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

圖片圖片

一般我們認(rèn)為,單表行數(shù)超過 500 萬行或者單表容量超過 2GB 時,才需要考慮做分庫分表。

那我們是不是等到數(shù)據(jù)量到達(dá) 500 萬后,才開始分庫分表呢?

這個也不絕對,應(yīng)該提前規(guī)劃分庫分表,如果估算 3 年后,表的數(shù)據(jù)量都不會到達(dá) 500 萬,則不需要分庫分表。

分庫分表的時候需要考慮數(shù)據(jù)未來 2~3 年的一個增量,即使現(xiàn)在數(shù)據(jù)量不多,但是每天的數(shù)據(jù)增量很可觀,幾個月之后就可以突破 500 萬上限,那么不是等到數(shù)據(jù)量到達(dá) 500 萬的時候才分庫分表,而是現(xiàn)在就應(yīng)該考慮了。

什么時候既分庫又分表?

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

橫向拆分 & 縱向拆分

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

通常在做拆分的時候有兩種分法,分別是橫向拆分(水平拆分)和縱向拆分(垂直拆分)。

假如我們有一張表,如果把這張表中某一條記錄的多個字段,拆分到多張表中,這種就是縱向拆分。那如果把一張表中的不同的記錄分別放到不同的表中,這種就是橫向拆分。

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

圖片圖片

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

圖片圖片

縱向拆分比較適合做冷熱分離,可以使得行數(shù)據(jù)變小,一個數(shù)據(jù)頁就能存放更多的數(shù)據(jù),在查詢時就會減少I/O次數(shù)。

分表算法

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

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

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

Range 范圍

Range,即范圍策略劃分表。比如我們可以將表的主鍵 order_id,按照從 0~300萬 的劃分為一個表,300萬 ~ 600萬劃分到另外一個表。

有時候我們也可以按時間范圍來劃分,如不同年月的訂單放到不同的表。

  • 優(yōu)點(diǎn):范圍分表,有利于擴(kuò)容。
  • 缺點(diǎn):最近一段時間的數(shù)據(jù)都是匯聚在一張表里面,可能會有熱點(diǎn)問題。比如最近一個月的訂單都在 0~300萬之間,平時用戶一般都查最近一個月的訂單比較多,那么請求就都打到 order_01 了。

Hash 取模

Hash 取模策略:

指定的路由key(一般是 user_id、order_id 等作為key)對分表總數(shù)進(jìn)行取模,把數(shù)據(jù)分散到各個表中。

比如原始訂單表信息,我們把它分成4張分表:

圖片圖片

比如 id=1,對 4 取模,就會得到1,就把它放到 t_order_1 ;

一般,我們會取哈希值,再做取余:

Math.abs(orderId.hashCode()) % table_number

  • 優(yōu)點(diǎn):Hash取模的方式,不會存在明顯的熱點(diǎn)問題。
  • 缺點(diǎn):如果未來某個時候,表數(shù)據(jù)量又到瓶頸了,需要擴(kuò)容,就比較麻煩。所以一般建議提前規(guī)劃好,一次性分夠(可以考慮一致性哈希)。

一致性 Hash

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

圖片圖片

一致性哈??梢园凑粘S玫?Hash 算法來將對應(yīng)的 key 哈希到一個具有 2^32 次方個節(jié)點(diǎn)的空間中,形成一個順時針首尾相接的閉合環(huán)形,這個環(huán)稱為哈希環(huán)。

當(dāng)添加一臺新的數(shù)據(jù)庫服務(wù)器時,只有增加服務(wù)器的位置和逆時針方向第一臺服務(wù)器之間的鍵會受影響。

簡單來說,一致性哈希算法能夠使機(jī)器節(jié)點(diǎn)的變動對整個集群的影響達(dá)到最小。

一致性哈希也存在一些問題,如:節(jié)點(diǎn)漂移、數(shù)據(jù)傾斜。這些都有對應(yīng)的解決方案,這里不再贅述。

參考:一致性哈希問題及其解決方案。

斐波那契散列

前面幾種分表算法,大家會接觸多一點(diǎn),斐波那契散列實(shí)際在分表算法中幾乎不被使用。

JDK 的 ThreadLocal 源碼中有一段有意思的代碼,如下所示:

圖片圖片

定義了一個魔法值 HASH_INCREMENT = 0x61c88647,這個值被稱之為 “魔數(shù)”。

0x61c88647 與一個神奇的數(shù)字產(chǎn)生了聯(lián)系,它就是 (Math.sqrt(5) - 1)/2。也就是傳說中的黃金比例 0.618(0.618 只是一個粗略值),即0x61c88647 = 2^32 * 黃金分割比,同時也對應(yīng)了上文提到的斐波那契散列。

它常用于在散列中增加哈希值。上面的代碼注釋中也解釋到是為了讓哈希碼能均勻的分布在 2 的 N 次方的數(shù)組里。

至于為什么使用斐波那契數(shù)列后散列更均勻,就涉及到相關(guān)數(shù)學(xué)問題了,此處不做更多解釋。

嚴(yán)格雪崩標(biāo)準(zhǔn)(SAC)

上面介紹了一些分表算法,那么一個好的分表算法有沒有參考標(biāo)準(zhǔn)呢?

在密碼學(xué)中,雪崩效應(yīng)avalanche effect)指加密算法的一種理想屬性。雪崩效應(yīng)是指當(dāng)輸入發(fā)生最微小的改變(例如,反轉(zhuǎn)一個二進(jìn)制位)時,也會導(dǎo)致輸出的不可區(qū)分性改變(輸出中每個二進(jìn)制位有50%的概率發(fā)生反轉(zhuǎn))。

嚴(yán)格雪崩標(biāo)準(zhǔn)(SAC),建立于密碼學(xué)的完全性概念上,是雪崩效應(yīng)的形式化。它指出,當(dāng)任何一個輸入位被反轉(zhuǎn)時,輸出中的每一位均有 50% 的概率發(fā)生變化。

簡單來說,當(dāng)我們對數(shù)據(jù)庫從 8庫32表 擴(kuò)容到 16庫32表 的時候,每一個表中的數(shù)據(jù)總量都應(yīng)該以 50% 的數(shù)量進(jìn)行減少。這樣才是合理的。

引入嚴(yán)格雪崩標(biāo)準(zhǔn)(SAC) 之后,斐波那契散列是不滿足這個標(biāo)準(zhǔn)的,也就是說使用斐波那契散列,在分庫分表擴(kuò)容情況下,可能導(dǎo)致數(shù)據(jù)分布不均勻,這也是為什么斐波那契散列幾乎不用于分表算法的原因。

訂單分庫分表實(shí)戰(zhàn)

背景:訂單表的讀寫場景復(fù)雜,?般有買家維度、賣家維度、訂單號維度 3 個主要維度。多讀寫維度情況下?論采取哪種維度做分庫分表,對另外兩種維度的查詢性能來說,基本都是災(zāi)難。

解決方案:雙拆分列哈希(RANGE_HASH)。

選取兩個拆分鍵,兩個拆分鍵的后 N 位需確保一致,根據(jù)任一拆分鍵后 N 位計算哈希值,然后再按分庫數(shù)取模,完成路由計算。

先采用 RANGE_HASH 拆分算法按買家 id 后 N 位、訂單號后 N 位維度做分庫分表,作為買家表邏輯表。再用 HASH 拆分函數(shù)按商家 id 冗余一份數(shù)據(jù),作為賣家表邏輯表。

訂單號生成規(guī)則需要根據(jù)買家表分表特性訂單號后 N 位等于買家 id 后 N 位做設(shè)計。

比如用戶id為 12345678,則用戶在下單時生成的單號為:xxxxxxxxx345678,單號前幾位可以根據(jù)公司自己規(guī)則設(shè)定,但是要注意不能重復(fù)。

全局 ID 的生成

涉及到分庫分表,就會引申出分布式系統(tǒng)中唯一主鍵 ID 的生成問題,有以下幾種方式:

UUID

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

基于某個單表做自增主鍵

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

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

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

雪花算法

圖片圖片

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

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

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

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

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

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

時間回?fù)軉栴}

熟悉雪花算法的可能了解到雪花算法存在名為“時間回?fù)堋?的問題。

時間回?fù)埽河捎跈C(jī)器的時間是動態(tài)調(diào)整的,有可能會出現(xiàn)時間跑到之前幾毫秒,如果這個時候獲取到了這種時間,則會出現(xiàn)數(shù)據(jù)重復(fù)。

時間回?fù)軉栴}解決思路可以參考美團(tuán)開源的 Leaf。

美團(tuán) Leaf 引入了 Zookeeper 來解決時鐘回?fù)軉栴},其大致思路為:每個 Leaf 運(yùn)行時定時向 zk 上報時間戳。每次 Leaf 服務(wù)啟動時,先校驗(yàn)本機(jī)時間與上次發(fā) ID 的時間,再校驗(yàn)與 zk 上所有節(jié)點(diǎn)的平均時間戳。如果任何一個階段有異常,那么就啟動失敗報警。

這個解決方案還是比較好理解的,就是對比上次發(fā) ID 的時間,還有其他機(jī)器的平均時間,通過本地存儲時間戳 + 定時上報時間戳的方式,解決了時間回?fù)艿膯栴}。

分庫分表遷移

有一個未分庫分表的系統(tǒng),現(xiàn)在要分庫分表,如何才可以讓系統(tǒng)從未分庫分表切換到分庫分表上?

停機(jī)遷移方案

先說一個最 low 的方案,就是很簡單,大伙凌晨 12點(diǎn) 開始運(yùn)維,網(wǎng)站或者 app 掛個公告,說 0 點(diǎn)到早上 6 點(diǎn)進(jìn)行服務(wù)器維護(hù),無法訪問......

接著到 0 點(diǎn),停機(jī),系統(tǒng)停掉,沒有流量寫入了,此時老的單庫單表數(shù)據(jù)庫靜止了。然后提前寫好一個導(dǎo)數(shù)的一次性工具,此時直接跑起來,然后將單庫單表的數(shù)據(jù)讀出來,寫到分庫分表里面去。

導(dǎo)數(shù)完了之后,就 ok 了,修改系統(tǒng)的數(shù)據(jù)庫連接配置啥的,包括可能代碼和 SQL 也許有修改,那你就用最新的代碼,然后直接啟動連到新的分庫分表上去。

但是這個方案比較 low,有個致命的問題就是業(yè)務(wù)要中斷,來看看高大上一點(diǎn)的方案。

雙寫遷移方案

這個是常用的一種遷移方案,比較靠譜一些,不用停機(jī)。

大致步驟如下:

  1. 先改造我們的數(shù)據(jù)寫入端, 使數(shù)據(jù)同時寫入舊數(shù)據(jù)庫和新數(shù)據(jù)庫。
  2. 對存量數(shù)據(jù)進(jìn)行不停機(jī)的遷移。
  3. 等到雙寫服務(wù)運(yùn)行一段時間,再次進(jìn)行舊數(shù)據(jù)和新數(shù)據(jù)的校驗(yàn)同步。
  4. 完全切換讀取的數(shù)據(jù)源為新數(shù)據(jù)庫,關(guān)閉舊數(shù)據(jù)庫的寫入和讀取,下線舊數(shù)據(jù)庫。

這種方式的好處是:遷移的過程可以隨時回滾,將遷移的風(fēng)險降到了最低。劣勢是:時間周期比較長,應(yīng)用有改造的成本。

分庫分表帶來的問題

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

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

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

圖片圖片

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

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

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

責(zé)任編輯:武曉燕 來源: Java隨想錄
相關(guān)推薦

2022-12-09 09:21:10

分庫分表算法

2022-03-08 08:39:22

gRPC協(xié)議云原生

2010-03-16 17:07:51

云計算

2023-07-26 13:11:21

ChatGPT平臺工具

2024-01-19 08:25:38

死鎖Java通信

2024-02-04 00:00:00

Effect數(shù)據(jù)組件

2023-01-10 08:43:15

定義DDD架構(gòu)

2024-04-01 08:29:09

Git核心實(shí)例

2023-09-12 07:26:46

2024-01-02 12:05:26

Java并發(fā)編程

2023-08-01 12:51:18

WebGPT機(jī)器學(xué)習(xí)模型

2023-01-30 09:01:54

圖表指南圖形化

2023-10-10 11:04:11

Rust難點(diǎn)內(nèi)存

2023-12-12 08:02:10

2024-05-06 00:00:00

InnoDBView隔離

2024-08-06 09:47:57

2022-07-08 09:27:48

CSSIFC模型

2024-07-31 08:39:45

Git命令暫存區(qū)

2023-02-15 08:41:56

多層維表性能寬表

2024-07-10 08:15:40

點(diǎn)贊
收藏

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