這篇文章的內(nèi)容主要圍繞怎樣淺談分布式ID的實(shí)踐與應(yīng)用進(jìn)行講述,文章內(nèi)容清晰易懂,條理清晰,非常適合新手學(xué)習(xí),值得大家去閱讀。感興趣的朋友可以跟隨小編一起閱讀吧。希望大家通過(guò)這篇文章有所收獲!
站在用戶的角度思考問(wèn)題,與客戶深入溝通,找到越城網(wǎng)站設(shè)計(jì)與越城網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗(yàn),讓設(shè)計(jì)與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個(gè)性化、用戶體驗(yàn)好的作品,建站類型包括:網(wǎng)站設(shè)計(jì)、網(wǎng)站制作、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、域名與空間、虛擬空間、企業(yè)郵箱。業(yè)務(wù)覆蓋越城地區(qū)。
在業(yè)務(wù)系統(tǒng)中很多場(chǎng)景下需要生成不重復(fù)的 ID,比如訂單編號(hào)、支付流水單號(hào)、優(yōu)惠券編號(hào)等都需要使用到。小編將介紹分布式 ID 的產(chǎn)生原因,以及目前業(yè)界常用的四種分布式 ID 實(shí)現(xiàn)方案,并且詳細(xì)介紹其中兩種的實(shí)現(xiàn)以及優(yōu)缺點(diǎn),
隨著業(yè)務(wù)數(shù)據(jù)量的增長(zhǎng),存儲(chǔ)在數(shù)據(jù)庫(kù)中的數(shù)據(jù)越來(lái)越多,當(dāng)索引占用的空間超出可用內(nèi)存大小后,就會(huì)通過(guò)磁盤索引來(lái)查找數(shù)據(jù),這樣就會(huì)極大的降低數(shù)據(jù)查詢速度。如何解決這樣的問(wèn)題呢?一般我們首先通過(guò)分庫(kù)分表來(lái)解決,分庫(kù)分表后就無(wú)法使用數(shù)據(jù)庫(kù)自增 ID 來(lái)作為數(shù)據(jù)的唯一編號(hào),那么就需要使用分布式 ID 來(lái)做唯一編號(hào)了。
目前,關(guān)于分布式 ID ,業(yè)界主要有以下四種實(shí)現(xiàn)方案:
UUID:使用 JDK 的 UUID#randomUUID() 生成的 ID;
redis 的原子自增:使用 Jedis#incr(String key) 生成的 ID;
Snowflake 算法:以時(shí)間戳機(jī)器號(hào)和毫秒內(nèi)并發(fā)組成的 64 位 Long 型 ID;
分段步長(zhǎng):按照步長(zhǎng)從數(shù)據(jù)庫(kù)讀取一段可用范圍的 ID;
我們總結(jié)一下這幾種方案的特點(diǎn):
方案 | 順序性 | 重復(fù)性 | 可用性 | 部署方式 | 可用時(shí)間 |
---|---|---|---|---|---|
UUID | 無(wú)序 | 通過(guò)多位隨機(jī)字符達(dá)到極低重復(fù)概率,但理論上是會(huì)重復(fù)的 | 一直可用 | JDK 直接調(diào)用 | 永久 |
Redis | 單調(diào)遞增 | RDB 持久化模式下,會(huì)出現(xiàn)重復(fù) | Redis 宕機(jī)后不可用 | Jedis 客戶端調(diào)用 | 永久 |
Snowflake | 趨勢(shì)遞增 | 不會(huì)重復(fù) | 發(fā)生時(shí)鐘回?fù)懿⑶一負(fù)軙r(shí)間超過(guò)等待閾值時(shí)不可用 | 集成部署、集群部署 | 69年 |
分段步長(zhǎng) | 趨勢(shì)遞增 | 不會(huì)重復(fù) | 如果數(shù)據(jù)庫(kù)宕機(jī)并且獲取步長(zhǎng)內(nèi)的 ID 用完后不可用 | 集成部署、集群部署 | 永久 |
前面兩種實(shí)現(xiàn)方案的用法以及實(shí)現(xiàn)大家日常了解較多,就不在此贅述...本文我們會(huì)詳細(xì)介紹 Snowflake 算法以及分段步長(zhǎng)方案。
Snowflake 算法可以做到分配好機(jī)器號(hào)后就可以使用,不依賴任何第三方服務(wù)實(shí)現(xiàn)本地 ID 生成,依賴的第三方服務(wù)越少可用性越高,那么我們先來(lái)介紹一下 Snowflake 算法。
長(zhǎng)整型數(shù)字(即 Long 型數(shù)字)的十進(jìn)制范圍是 -2^64 到 2^64-1。
Snowflake 使用的是無(wú)符號(hào)長(zhǎng)整型數(shù)字,即從左到右一共 64 位二進(jìn)制組成,但其第一位是不使用的。所以,在 Snowflake 中使用的是 63bit 的長(zhǎng)整型無(wú)符號(hào)數(shù)字,它們由時(shí)間戳、機(jī)器號(hào)、毫秒內(nèi)并發(fā)序列號(hào)三個(gè)部分組成 :
時(shí)間戳位:當(dāng)前毫秒時(shí)間戳與新紀(jì)元時(shí)間戳的差值(所謂新紀(jì)元時(shí)間戳就是應(yīng)用開(kāi)始使用 Snowflake 的時(shí)間。如果不設(shè)置新紀(jì)元時(shí)間,時(shí)間戳默認(rèn)是從1970年開(kāi)始計(jì)算的,設(shè)置了新紀(jì)元時(shí)間可以延長(zhǎng) Snowflake 的可用時(shí)間)。41 位 2 進(jìn)制轉(zhuǎn)為十進(jìn)制是 2^41,除以(365 天 * 24 小時(shí) * 3600 秒 * 1000 毫秒),約等于 69年,所以最多可以使用 69 年;
機(jī)器號(hào):10 位 2 進(jìn)制轉(zhuǎn)為十進(jìn)制是 2^10,即 1024,也就是說(shuō)最多可以支持有 1024 個(gè)機(jī)器節(jié)點(diǎn);
毫秒內(nèi)并發(fā)序列號(hào):12 位 2 進(jìn)制轉(zhuǎn)為十進(jìn)制是 2^12,即 4096,也就是說(shuō)一毫秒內(nèi)在一個(gè)機(jī)器節(jié)點(diǎn)上并發(fā)的獲取 ID,最多可以支持 4096 個(gè)并發(fā);
下面我們來(lái)看一下各個(gè)分段的使用情況:
二進(jìn)制分段 | [1] | [2, 42] | [43, 52] | [53, 64] |
---|---|---|---|---|
說(shuō)明 | 最高符號(hào)位不使用 | 一共41位,是毫秒時(shí)間戳位 | 一個(gè)10位,是機(jī)器號(hào)位 | 一共12位,是毫秒內(nèi)并發(fā)序列號(hào),當(dāng)前請(qǐng)求的時(shí)間戳如果和上一次請(qǐng)求的時(shí)間戳相同,那么就將毫秒內(nèi)并發(fā)序列號(hào)加一 |
那么 Snowflake 生成的 ID 長(zhǎng)什么樣子呢?下面我們來(lái)舉幾個(gè)例子(假設(shè)我們的時(shí)間戳新紀(jì)元是 2020-12-31 00:00:00):
時(shí)間 | 機(jī)器號(hào) | 毫秒并發(fā) | 十進(jìn)制 Snowflake ID |
---|---|---|---|
2021-01-01 08:33:11 | 1 | 10 | 491031363588106 |
2021-01-02 13:11:12 | 2 | 25 | 923887730696217 |
2021-01-03 21:22:01 | 3 | 1 | 1409793654796289 |
Snowflake 可以使用三種不同的部署方式來(lái)部署,集成分布式部署方式、中心集群式部署方式、直連集群式部署方式。下面我們來(lái)分別介紹一下這幾種部署方式。
當(dāng)使用 ID 的應(yīng)用節(jié)點(diǎn)比較少時(shí),比如 200 個(gè)節(jié)點(diǎn)以內(nèi),適合使用集成分布式部署方式。每個(gè)應(yīng)用節(jié)點(diǎn)在啟動(dòng)的時(shí)候決定了機(jī)器號(hào)后,運(yùn)行時(shí)不依賴任何第三方服務(wù),在本地使用時(shí)間戳、機(jī)器號(hào)、以及毫秒內(nèi)并發(fā)序列號(hào)生成 ID。
下圖展示的是應(yīng)用服務(wù)器通過(guò)引入 jar 包的方式實(shí)現(xiàn)獲取分布式 ID 的過(guò)程。每一個(gè)使用分布式 ID 的應(yīng)用服務(wù)器節(jié)點(diǎn)都會(huì)分配一個(gè)拓?fù)渚W(wǎng)絡(luò)內(nèi)唯一的機(jī)器號(hào)。這個(gè)機(jī)器號(hào)的管理存放在 MySQL 或者 ZooKeeper 上。
當(dāng)拓?fù)渚W(wǎng)絡(luò)內(nèi)使用分布式 ID 的機(jī)器節(jié)點(diǎn)很多,例如超過(guò) 1000 個(gè)機(jī)器節(jié)點(diǎn)時(shí),使用集成部署的分布式 ID 就不合適了,因?yàn)闄C(jī)器號(hào)位一共是 10 位,即最多支持 1024 個(gè)機(jī)器號(hào)。當(dāng)機(jī)器節(jié)點(diǎn)超過(guò) 1000 個(gè)機(jī)器節(jié)點(diǎn)時(shí),可以使用下面要介紹的中心集群式部署方式。
中心集群式部署需要新增用來(lái)做請(qǐng)求轉(zhuǎn)發(fā)的 ID 網(wǎng)關(guān),比如使用 nginx 反向代理(即下圖中的 ID REST API Gateway)。
使用 ID 網(wǎng)關(guān)組網(wǎng)后,應(yīng)用服務(wù)器通過(guò) HTTP 或 RPC 請(qǐng)求 ID 網(wǎng)關(guān)獲取分布式 ID。這樣相比于上面的集成分布式部署方式,就可以支撐更多的應(yīng)用節(jié)點(diǎn)使用分布式 ID 了。
如圖所示,機(jī)器號(hào)的分配只是分配給下圖中的 ID Generator node 節(jié)點(diǎn),應(yīng)用節(jié)點(diǎn)是不需要分配機(jī)器號(hào)的。
使用中心集群式部署方式需要引入新的 nginx 反向代理做網(wǎng)關(guān),增加了系統(tǒng)的復(fù)雜性,降低了服務(wù)的可用性。那么我們下面再介紹一種不需要引入 nginx 又可以支持超過(guò) 1000 個(gè)應(yīng)用節(jié)點(diǎn)的直連集群部署方式。
相比于中心集群部署方式,直連集群部署方式可以去掉中間的 ID 網(wǎng)關(guān),提高服務(wù)的可用性。
在使用 ID 網(wǎng)關(guān)的時(shí)候,我們需要把 ID generator node 的服務(wù)地址配置在 ID 網(wǎng)關(guān)中。而在使用直連集群式部署方式時(shí),ID generator node 的服務(wù)地址可以配置在應(yīng)用服務(wù)器本地配置文件中,或者配置在配置中心。應(yīng)用服務(wù)器獲取到服務(wù)地址列表后,需要實(shí)現(xiàn)服務(wù)路由,直連 ID 生成器獲取 ID。
Snowflake 算法是強(qiáng)依賴時(shí)間戳的算法,如果一旦發(fā)生時(shí)鐘回?fù)芫蜁?huì)產(chǎn)生 ID 重復(fù)的問(wèn)題。那么時(shí)鐘回?fù)苁窃趺串a(chǎn)生的,我們又需要怎么去解決這個(gè)問(wèn)題呢?
NTP(Network Time Protocol)服務(wù)自動(dòng)校準(zhǔn)可能導(dǎo)致時(shí)鐘回?fù)?。我們身邊的每一臺(tái)計(jì)算機(jī)都有自己本地的時(shí)鐘,這個(gè)時(shí)鐘是根據(jù) CPU 的晶振脈沖計(jì)算得來(lái)的,然而隨著運(yùn)行時(shí)間的推移,這個(gè)時(shí)間和世界時(shí)間的偏差會(huì)越來(lái)越大,那么 NTP 就是用來(lái)做時(shí)鐘校準(zhǔn)的服務(wù)。
一般情況下發(fā)生時(shí)鐘回?fù)艿母怕室卜浅P?,因?yàn)橐坏┏霈F(xiàn)本地時(shí)間相對(duì)于世界時(shí)間需要校準(zhǔn),但時(shí)鐘偏差值小于 STEP 閾值(默認(rèn)128毫秒)時(shí),計(jì)算機(jī)會(huì)選擇以 SLEW 的方式進(jìn)行同步,即以 0.5 毫秒/秒的速度差調(diào)整時(shí)鐘速度,保證本地時(shí)鐘是一直連續(xù)向前的,不產(chǎn)生時(shí)鐘回?fù)?,直到本地時(shí)鐘和世界時(shí)鐘對(duì)齊。
然而如果本地時(shí)鐘和世界時(shí)鐘相差大于 STEP 閾值時(shí),就會(huì)發(fā)生時(shí)鐘回?fù)?。這個(gè) STEP 閾值是可以修改的,但是修改的越大,在 SLEW 校準(zhǔn)的時(shí)候需要花費(fèi)的校準(zhǔn)時(shí)間就越長(zhǎng),例如 STEP 閾值設(shè)置為 10 分鐘,即本地時(shí)鐘與世界時(shí)鐘偏差在 10 分鐘以內(nèi)時(shí)都會(huì)以 SLEW 的方式進(jìn)行校準(zhǔn),這樣最多會(huì)需要 14 天才會(huì)完成校準(zhǔn)。
為了避免時(shí)鐘回?fù)軐?dǎo)致重復(fù) ID 的問(wèn)題,可以使用 128 毫秒的 STEP 閾值,同時(shí)在獲取 SnowflakeID 的時(shí)候與上一次的時(shí)間戳相比,判斷時(shí)鐘回?fù)苁欠裨?1 秒鐘以內(nèi),如果在 1 秒鐘以內(nèi),那么等待 1 秒鐘,否則服務(wù)不可用,這樣可以解決時(shí)鐘回?fù)?1 秒鐘的問(wèn)題。
Snowflake 由于是將時(shí)間戳作為長(zhǎng)整形的高位,所以導(dǎo)致生成的最小數(shù)字也非常大。比如超過(guò)時(shí)間新紀(jì)元 1 秒鐘,機(jī)器號(hào)為 1,毫秒并發(fā)序列為 1 時(shí),生成的 ID 就已經(jīng)到 4194308097 了。那么有沒(méi)有一種方法能夠?qū)崿F(xiàn)在初始狀態(tài)生成數(shù)字較小的 ID 呢?答案是肯定的,下面來(lái)介紹一下分段步長(zhǎng) ID 方案。
使用分段步長(zhǎng)來(lái)生成 ID 就是將步長(zhǎng)和當(dāng)前最大 ID 存在數(shù)據(jù)庫(kù)中,每次獲取 ID 時(shí)更新數(shù)據(jù)庫(kù)中的 ID 最大值增加步長(zhǎng)。
數(shù)據(jù)庫(kù)核心表結(jié)構(gòu)如下所示:
CREATE TABLE `segment_id` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `biz_type` varchar(64) NOT NULL DEFAULT '', // 業(yè)務(wù)類型 `max` bigint(20) DEFAULT '0', // 當(dāng)前最大 ID 值 `step` bigint(20) DEFAULT '10000', // ID 步長(zhǎng) PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
在獲取 ID 時(shí),使用開(kāi)啟事務(wù),利用行鎖保證讀取到當(dāng)前更新的最大 ID 值:
start transaction; update segment_id set max = max + step where biz_type = 'ORDER'; select max from segment_id where biz_type = 'ORDER'; commit
分段步長(zhǎng) ID 生成方案的優(yōu)缺點(diǎn):
優(yōu)點(diǎn):ID 生成不依賴時(shí)間戳,ID 生成初始值可以從 0 開(kāi)始逐漸增加;
缺點(diǎn):當(dāng)服務(wù)重啟時(shí)需要將最大 ID 值增加步長(zhǎng),頻繁重啟的話就會(huì)浪費(fèi)掉很多分段。
上文介紹了 Snowflake 算法以及分段步長(zhǎng)方案,他們各有優(yōu)缺點(diǎn),針對(duì)他們各自的情況我們?cè)诒疚囊步o出相應(yīng)的優(yōu)化方案。
為了提高 SnowflakeID的并發(fā)性能和可用性,可以使用 ID 緩沖環(huán)(即 ID Buffer Ring)。提高并發(fā)性提現(xiàn)在通過(guò)使用緩沖環(huán)能夠充分利用毫秒時(shí)間戳,提高可用性提現(xiàn)在可以相對(duì)緩解由時(shí)鐘回?fù)軐?dǎo)致的服務(wù)不可用。緩沖環(huán)是通過(guò)定長(zhǎng)數(shù)組加游標(biāo)哈希實(shí)現(xiàn)的,相比于鏈表會(huì)不需要頻繁的內(nèi)存分配。
在 ID 緩沖環(huán)初始化的時(shí)候會(huì)請(qǐng)求 ID 生成器將 ID 緩沖環(huán)填滿,當(dāng)業(yè)務(wù)需要獲取 ID 時(shí),從緩沖環(huán)的頭部依次獲取 ID。當(dāng) ID 緩沖環(huán)中剩余的 ID 數(shù)量少于設(shè)定的閾值百分比時(shí),比如剩余 ID 數(shù)量少于整個(gè) ID 緩沖環(huán)的 30% 時(shí),觸發(fā)異步 ID 填充加載。異步 ID 填充加載會(huì)將新生成的 ID 追加到 ID 緩沖環(huán)的隊(duì)列末尾,然后按照哈希算法映射到 ID 緩沖環(huán)上。另外有一個(gè)單獨(dú)的定時(shí)器異步線程來(lái)定時(shí)填充 ID 緩沖環(huán)。
下面的動(dòng)畫(huà)展示了 ID 緩沖環(huán)的三個(gè)階段:ID 初始化加載、ID 消費(fèi)、ID 消費(fèi)后填充:
Buffer Ring Initialize load,ID 緩沖環(huán)初始化加載:從 ID generator 獲取到 ID 填充到 ID 緩沖環(huán),直到 ID 緩沖環(huán)被填滿;
Buffer Ring consume,ID 緩沖環(huán)消費(fèi):業(yè)務(wù)應(yīng)用從 ID 緩沖環(huán)獲取 ID;
Async reload,異步加載填充 ID 緩沖環(huán):定時(shí)器線程負(fù)責(zé)異步的從 ID generator 獲取 ID 添加到 ID 緩沖隊(duì)列,同時(shí)按照哈希算法映射到 ID 緩沖環(huán)上,當(dāng) ID 緩沖環(huán)被填滿時(shí),異步加載填充結(jié)束;
下面的流程圖展示了 ID 緩沖環(huán)的運(yùn)行的整個(gè)生命周期,其中:
IDConsumerServer:表示使用分布式 ID 的業(yè)務(wù)系統(tǒng);
IDBufferRing:ID 緩沖環(huán);
IDGenerator:ID 生成器;
IDBufferRingAsyncLoadThread:異步加載 ID 到緩沖環(huán)的線程;
Timer:負(fù)責(zé)定時(shí)向異步加載線程添加任務(wù)來(lái)裝載 ID;
ID 消費(fèi)流程:即 上面提到的 Buffer Ring consume;
整體流程:客戶端業(yè)務(wù)請(qǐng)求到應(yīng)用服務(wù)器,應(yīng)用服務(wù)器從 ID 緩沖環(huán)獲取 ID,如果 ID 緩沖環(huán)內(nèi)空了那么拋出服務(wù)不可用;如果 ID 緩沖環(huán)內(nèi)存有 ID 那么就消費(fèi)一個(gè) ID 。同時(shí)在消費(fèi) ID 緩沖環(huán)中的 ID 時(shí),如果發(fā)現(xiàn) ID 緩沖環(huán)中存留的 ID 數(shù)量少于整個(gè) ID 緩沖環(huán)容量的 30% 時(shí)觸發(fā)異步加載填充 ID 緩沖環(huán)。
在使用分段步長(zhǎng) ID時(shí),如果該分段的 ID 用完了,需要更新數(shù)據(jù)庫(kù)分段最大值再繼續(xù)提供 ID 生成服務(wù),為了減少數(shù)據(jù)庫(kù)更新查詢可能帶來(lái)的延時(shí)對(duì) ID 服務(wù)的性能影響,可以使用雙桶緩存方案來(lái)提高 ID 生成服務(wù)的可用性。
其主要原理:設(shè)計(jì)兩個(gè)緩存桶:currentBufferBucket 和 nextBufferBucket,每個(gè)桶都存放一個(gè)步長(zhǎng)這么多的 ID,如果當(dāng)前緩存桶的 ID 用完了,那么就將下一個(gè)緩存桶設(shè)置為當(dāng)前緩存桶。
下面的動(dòng)畫(huà)展示了雙桶緩存初始化、異步加載預(yù)備桶和將預(yù)備桶切換成當(dāng)前桶的全過(guò)程:
Current bucket initial load:初始化當(dāng)前的緩存桶,即更新 max = max + step,然后獲取更新后的 max 值,比如步長(zhǎng)是 1000,更新后的 max 值是 1000,那么桶的高度就是步長(zhǎng)即 1000,桶 min = max - step + 1 = 1,max = 1000;
Current bucket remaining id count down to 20%,Next bucket start to load。當(dāng)前緩存桶的 ID 剩余不足 20% 的時(shí)候可以加載下一個(gè)緩存桶,即更新 max = max + step,后獲取更新后的 max 值,此時(shí)更新后的 max 值是 2000,min = max - step + 1 = 1001, max = 2000;
Current bucket is drained,Switch current bucket to the next bucket,如果當(dāng)前桶的 ID 全部用完了,那么就將下一個(gè) ID 緩存桶設(shè)置為當(dāng)前桶;
下面是雙桶 Buffer 的流程圖:
感謝你的閱讀,相信你對(duì)“怎樣淺談分布式ID的實(shí)踐與應(yīng)用”這一問(wèn)題有一定的了解,快去動(dòng)手實(shí)踐吧,如果想了解更多相關(guān)知識(shí)點(diǎn),可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站!小編會(huì)繼續(xù)為大家?guī)?lái)更好的文章!
網(wǎng)站名稱:怎樣淺談分布式ID的實(shí)踐與應(yīng)用
文章來(lái)源:http://jinyejixie.com/article36/iisipg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供ChatGPT、網(wǎng)站改版、商城網(wǎng)站、云服務(wù)器、App開(kāi)發(fā)、定制網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)