獲取電腦唯一機器碼 分布式系統唯一ID的生產

2021-10-12 08:16:49 字數 3320 閱讀 4696

為什麼需要分布式id呢?

因為分布式架構下,唯一序列號生成是我們在設計乙個系統,尤其是資料庫使用分庫分表的時候常常會遇見的問題。當分成若干個sharding表後,如何能夠快速拿到乙個唯一序列號,是經常遇到的問題。總而言之, 是因為高併發, 我們引入了分庫分表, 所以需要分布式唯一id的生產.

全域性唯一,這是基本要求,不能出現重複。

數字型別,趨勢遞增,後面的id必須比前面的大,這是從mysql儲存引擎來考慮的,需要保證寫入資料的效能。

長度短,能夠提高查詢效率,這也是從mysql資料庫規範出發的,尤其是id作為主鍵時。

資訊保安,如果id連續生成,勢必會洩露業務資訊,甚至可能被猜出,所以需要無規則不規則。

高可用低延時,id生成快,能夠扛住高併發,延時足夠低不至於成為業頸。

基於uuid

優點:缺點:

基於變種uuid

針對缺點, 可以通過方法,  將時間放在前面

基於資料庫多例項主鍵自增

需要關注步長step 和 例項個數, 如下圖所示:

**:從上圖可以看出,水平擴充套件的資料庫集群,有利於解決資料庫單點壓力的問題,同時為了id生成特性,將自增步長按照機器數量來設定,但是,這裡有個缺點就是不能再擴容了,如果再擴容,id就沒法兒生成了,步長都用光了,那如果你要解決新增機器帶來的問題,你或許可以將第三台機器的id起始生成位置設定離現在的id比較遠的位置,同時把新的步長設定進去,同時修改舊機器上id生成的步長,但必須在id還沒有增長到新增機器設定的開始自增id值,否則就要出現重複了。

優點缺點

適用場景

這種方案,除了難以適應大規模分布式和高併發的場景,普通的業務規模還是能夠勝任的,所以這種方案還是值得積累。

基於redis生成id

當使用資料庫來生成id效能不夠要求的時候,我們可以嘗試使用redis來生成id。這主要依賴於redis是單執行緒的,所以也可以用生成全域性唯一的id。可以用redis的原子操作 incr和incrby來實現。

同樣需要預定義步長和初始化。假如乙個集群中有5臺redis。可以初始化每台redis的值分別是1,2,3,4,5,然後步長都是5。各個redis生成的id為:

a:1,6,11,16,21

b:2,7,12,17,22

c:3,8,13,18,23

d:4,9,14,19,24

e:5,10,15,20,25

優點:依賴redis效能較好

缺點:需要編碼, 增加工作量, 依賴redis, 需要保證可靠性.

雪花演算法

snowflake是twitter開源的分布式id生成演算法,結果是乙個long型的id。其核心思想是:使用41bit作為毫秒數,10bit作為機器的id(5個bit是資料中心,5個bit的機器id),12bit作為毫秒內的流水號(意味著每個節點在每毫秒可以產生 4096 個 id),最後還有乙個符號位,永遠是0。具體實現的**可以參看下面是生成規則:

其中,1位識別符號,不使用且標記為0;41位時間戳,用來儲存時間戳的差值;10位機器碼,可以標識1024個機器節點,如果機器分機房部署(idc),這10位還可以拆分,比如5位表示機房id,5位表示機器id,這樣就有32*32種組合,一般來說是足夠了;最後的12位隨即序列,用來記錄毫秒內的計數,乙個節點就能夠生成4096個id序號。所以綜上所述,綜合計算下來,理論上snowflake演算法方案的qps大約為409.6w/s,效能足夠強悍了,而且這種方式,能夠確保集群中每個節點生成的id都是不同的,且區間內遞增。

優點缺點

實現**:

public class idworker

public long getdatacenterid()

public long gettimestamp()

public idworker(long workerid, long datacenterid, long sequence)

if (datacenterid > maxdatacenterid || datacenterid < 0)

this.workerid = workerid;

this.datacenterid = datacenterid;

this.sequence = sequence;

}// 這個是核心方法,通過呼叫nextid()方法,讓當前這台機器上的snowflake演算法程式生成乙個全域性唯一的id

public synchronized long nextid()

// 下面是說假設在同乙個毫秒內,又傳送了乙個請求生成乙個id

// 這個時候就得把seqence序號給遞增1,最多就是4096

if (lasttimestamp == timestamp)

} else

// 這兒記錄一下最近一次生成id的時間戳,單位是毫秒

lasttimestamp = timestamp;

// 這兒就是最核心的二進位制位運算操作,生成乙個64bit的id

// 先將當前時間戳左移,放到41 bit那兒;將機房id左移放到5 bit那兒;將機器id左移放到5 bit那兒;將序號放最後12 bit

// 最後拼接起來成乙個64 bit的二進位制數字,轉換成10進製就是個long型

return ((timestamp - twepoch) << timestampleftshift) |

(datacenterid << datacenteridshift) |

(workerid << workeridshift) | sequence;

}/**

* 當某一毫秒的時間,產生的id數 超過4095,系統會進入等待,直到下一毫秒,系統繼續產生id

* @param lasttimestamp

* @return

*/private long tilnextmillis(long lasttimestamp)

return timestamp;

}//獲取當前時間戳

private long timegen()

/*** main 測試類

* @param args

*/public static void main(string args)

}}

分布式系統全域性唯一ID

全域性的唯一流水id 可以將乙個請求在分布式系統中的流轉路徑聚合。生成唯一id有兩種方法 持久型 使用資料庫表自增欄位或者sequence 生成,為了提高效率,每個應用節點可以快取乙個批次的id 如果機器重啟則可能會損失一部分id 但是這並不會產生任何問題。時間型 一般由機器號 業務號 時間 單節點...

分布式系統唯一ID設計

目錄 1 概述 2 分布式唯一id特點 3 分布式唯一id傳統方案 3.1 uuid 3.2 資料庫生成 3.3 redis生成id 3.4 利用zookeeper生成唯一id 3.5 snowflake 雪花演算法 方案 在複雜分布式系統中,往往需要對大量的資料和訊息進行唯一標識。如在金融 電商 ...

分布式系統全域性唯一ID生成

在複雜分布式系統中,往往需要對大量的資料和訊息進行唯一標識。如在金融 電商 支付 等產品的系統中,資料日漸增長,對資料分庫分表後需要有乙個唯一id來標識一條資料或訊息,資料庫的自增id顯然不能滿足需求,此時乙個能夠生成全域性唯一id的系統是非常必要的。同時除了對id號碼自身的要求,業務還對id號生成...