首頁>技術>

在分散式系統中,做事務跟蹤,資料分片,都需要使用全域性唯一ID。全域性唯一ID的生成方式需要滿足的需求一般包括:

時間有序。高可用。長度適中。容量。單調遞增。這個不是強需求,但是如果一系列ID自然有序,無論從效率,還是直觀感受上,都是非常友好的。但是也會帶來安全資訊洩露問題,比如每天的訂單數量。

實現思路

主要是下面四種思路

自增ID。需要一個系統維護所有的ID。隨機ID。產生一個隨機數。並不能保證絕對全域性唯一。號段ID。一種半自動的ID分配方式,就分配像電話號碼的區號,首先要規定每個例項的號段。時間戳,其實也是自增ID的一種,但是不用考慮起始ID,不需要記錄當前ID用於新ID生成。不過無形中也浪費了大量ID,同時要求所有的節點時間同步。

UUID

UUID一般是我們第一時間想到的全域性ID解決方案。RFC 4122描述了具體的規範實現。

UUID有128 bit長度,一般使用16進製表示,其形式是 8-4-4-4-12。

UUID的5個版本,我們要根據不同的場景決定使用哪種版本。版本間沒有精度好壞之分。

version 1, date-time & MAC addressversion 2, date-time & group/user idversion 3, MD5 hash & namespaceversion 4, pseudo-random numberversion 5, SHA-1 hash & namespace

常用的是版本1和4。版本1使用MAC地址作為Node ID;版本4使用偽隨機數作為Node ID。

版本1因為暴露了mac地址,所以有安全隱患被詬病。實際最常用的是版本4。

UUID的不足,由其長度和隨機數兩個方面導致:

無法口頭表達。太長的ID都有這個問題,疊加變化部分在中部,而不是開頭結尾,肉眼看去經常眼花。直接儲存字串會帶來儲存的浪費。特別是儲存格式是unicode的話。這個鍋當然不能完全由UUID背,也有程式設計考慮不周全的原因。UUID V4最後一段為隨機數,無法單調增長。沒有單調遞增。無法直接對ID進行排序。

第三方系統自增ID方案基於MySQL的方案

Flicker在解決全域性ID生成方案裡就採用了MySQL自增長ID的機制(auto_increment + replace into + MyISAM)。具體方法是:

伺服器端:建立資料庫,建表

CREATE  TABLE  Tickets64 (	id bigint(20) unsigned NOT NULL auto_increment,	stub char(1) NOT NULL default'',	PRIMARYKEY(id),	UNIQUEKEY stub (stub)) ENGINE=MyISAM

應用端:執行事務

REPLACE INTO Tickets64 (stub) VALUES('a');SELECT  LAST_INSERT_ID();

架構

使用兩臺資料庫,解決高可用問題透過區分auto_increment的起始值和步長,生成奇偶數的ID。基於Redis的方案簡單方案是用Redis的原子操作INCR和INCRBY實現。複雜方案是使用EVAL和EVALSHA執行Lua指令碼,結合TIME命令實現。

實現方案要考慮每個節點的生成速度,總的ID容量。

比如一個64位的ID,

41 bit存放時間,精確到毫秒,可存41年。

12bit 存放分片ID,最大分片是4095

10bit 存放自增ID,每個節點每毫秒可產生1024個ID

最終的ID計算公式是

((second * 1000 + microSecond / 1000) << (12 + 10)) + (shardId << 10) + seq;
基於MongoDB ObjectID的方案

MongoDB為每一個document自動生成_id屬性,稱為的ObjectID。其長度為12位元組,結構為:

4-byte value representing the seconds since the Unix epoch                 (which will not run out of seconds until the year 2106)3-byte machine identifier (usually derived from the MAC address),2-byte process id3-byte counter, starting with a random value.

具體有多種實現方案。詳細可參考官方文件(見最後)

single counter document to generate unique identifiers one at a timesingle counter document that allocates batches of unique identifiersmultiple counter documents that allocate batches of unique identifiersRandomly choose a unique identifier in the application and retry if it is already assigned

融合方案twitter snowflake

snowflake方案是2010年,比較早開源的分散式全域性ID生成方案。其使用“號段ID+時間戳+自增ID”實現。

其ID結構如下圖:

41位的時間序列(精確到毫秒,41位的長度可以使用69年)

10位的機器標識(10位的長度最多支援部署1024個節點)

12位的計數順序號(12位的計數順序號支援每個節點每毫秒產生4096個ID序號) 最高位是符號位,始終為0。

snowflake的各種實現有很多。我們也可以根據自己的需求,基於snowflake,設計自己的方案。比如baidu,嘀嘀,美團都基於此實現了自己的全域性ID方案。

1. 由於其中的機器標識需要提前設定,snowflake要想成為一種全自治的方案,必須實現機器標識的自動生成和全域性唯一,以適應現在auto scaling的要求。

2. 協議中缺乏版本號標識,不便於擴充套件升級

基於nginx的方案

nginx一般都是最前置的接入伺服器。可以利用nginx的內建變數,組合成一個全域性ID,插入Header中,供所有上游服務引用。比如

server{  set $trace_id "${pid}-${connection}-${connection_requests}-${request_id}";  if ($http_x_trace_id != "" ){      set $trace_id "${http_x_atrace_id}";  }  proxy_set_header x-trace-id $trace_id;    

在實際使用中,如果nginx伺服器是靜態配置的,不使用auto scaling group,我們也可以像snowflake為每一臺伺服器設定節點編號,降低重複機率,也便於識別ID來源。

總結

可用的方案包括

UUID基於第三方系統的ID自增方案,比如Redis/Mysql/MongoDB融合方案。比如snowflake/baidu uid/美團Leaf snowflake等

ID的生成方法,也可以根據場景不同,分為單個ID生成和批次ID生成兩種。

RFC4122:

https://tools.ietf.org/html/rfc4122

MongoDB:

https://www.mongodb.com/blog/post/generating-globally-unique-identifiers-for-use-with-mongodb

SnowFlake:

https://github.com/twitter-archive/snowflake

#分散式系統##開發##MySQL##Redis##Nginx#

2
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 坑爹的資料庫連線超時,讓人頭禿