在分佈式系統中,如何生成分佈式ID?

下面有幾種常用方案,可以根據具體業務場景來選擇。

1. UUID

UUID 指在一臺機器上生成的數字,它保證在同一時空中的所有機器都是唯一的。

UUID 由以下幾部分組成:

  • 當前日期和時間。
  • 時鐘序列。
  • 全局唯一的IEEE機器識別碼(如網卡MAC地址等)。
在分佈式系統中,如何生成分佈式ID?

在Java中,使用UUID 非常方便。

UUID uuid = UUID.randomUUID();

UUID 具有如下優點:

  • 使用方便,很容易實現。
  • 性能很高。

缺點:

  • 沒有順序,不能保證單調遞增。
  • 太長,總長32位,無論是存儲還是傳輸,缺點都比較明顯。

2. SnowFlake 雪花算法

Twitter 的SnowFlake 是一個非常優秀的ID生成方案,實現也比較簡單,8Byte 是一個Long,等於64bit,核心代碼就是毫秒級時間41位+10位機器ID+毫秒序列12位。

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

在分佈式系統中,如何生成分佈式ID?

SnowFlake 的優點如下:

  • 比UUID 短,一般為9-17位。
  • 生成的ID是數字,可以做到單調遞增。由於無法統一分佈式環境中每臺服務器的時鐘,它只能做到單臺機器單調遞增,無法做到全局遞增。
  • 性能非常出色,吞吐量達到幾十萬TPS。

SnowFlake的結構如下(每部分用-分開):

在分佈式系統中,如何生成分佈式ID?

0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000

第一位為未使用,接下來的41位為毫秒級時間(41位的長度可以使用69年),然後是5位DataCenterID和5位WorkerID(10位的長度最多支持部署1024個節點) ,最後12位是毫秒內的計數(12位的計數順序號支持每個節點每毫秒產生4096個ID序號)。

一共加起來剛好64位,為一個Long型。(轉換成字符串後長度最多19)

SnowFlake 生成的ID整體上按照時間自增排序,並且整個分佈式系統內不會產生ID碰撞(由DataCenterID和WorkerID作區分),並且效率較高。經測試SnowFlake 每秒能夠產生26萬個ID。

如下SnowFlake 算法生成的2進制和Long 型的唯一ID。

在分佈式系統中,如何生成分佈式ID?

關於《SnowFlake-Java版雪花算法》實現方案和演示,可以查看小編的另外一篇文章。

3. Ticket Server

Ticket Server 是 Flickr 採用的一種分佈式ID 生成方案,利用 MySQL 自增長ID 實現。

它的設計思路是利用數據庫中 auto increment 的特性和MySQL 特有的 REPLACE INTO 命令來實現,可以利用多臺MySQL 實現高擴展性和高可用性。

在分佈式系統中,如何生成分佈式ID?

比如以 2 臺MySQL 服務器實例,分別進行配置。

節點一配置:

auto_increment_increment = 2;

auto_increment_offset = 1;

節點二配置:

auto_increment_increment = 2;

auto_increment_offset = 2;

上述配置最好放到配置文件中,否則MySQL 服務器重啟將丟失設置。

然後分別在2個庫裡面,建表,如tickets,設置ENGINE=MyISAM,表級鎖,能保證所有 REPLACE INTO 的原子性。不斷通過 REPLACE INTO 促使ID 自增,這樣表中只有一條記錄。

REPLACE INTO tickets (stub) VALUES ('a')

在同一個連接內,通過 last insert id 獲取自增的ID 值。

Ticket Server 的優點:

  • 對於數據量不是特別大的應用場景,長度最小。
  • 如果已經有應用基於 MySQL 的自增ID,那麼採用此方案非常容易遷移,而且兼容性好。

這個方案沒有絕對的順序,只能有一個近似的順序,有可能在某個MySQL 實例的狀態跑得快。

總結

如果在高併發、大數據量的情況下,建議採用 SnowFlake 方案,性能突出。

對於並非不大,並且需要兼容老業務的場景下,可以使用 Ticket Server方案,2臺MySQL 物理機也能輕鬆達到 幾萬TPS,響應時間在毫秒級。


分享到:


相關文章: