分佈式 ID 生成方案之美團 Leaf

分佈式 ID

在龐大複雜的分佈式系統中,一般須要對海量數據進行惟一標識,隨着數據日漸增加,對數據分庫分表之後須要有一個惟一 ID 來標識一條數據,而數據庫的自增 ID 顯然不能知足需求,此時就須要有一個可以生成全局惟一 ID 的系統,須要知足如下條件:git

  • 全局惟一性:最基本的要求就是不能出現重複的 ID。
  • 遞增:保證下一個 ID 必定大於上一個 ID。
  • 信息安全:若是 ID 是連續的,用戶就能夠按照順序進行惡意爬取數據,因此 ID 生成無規則。

上述的 2 和 3 點需求是互斥的,沒法使用同一個方案知足。github

生成方案

數據庫生成

以 MySQL 爲例,利用給字段設置 auto_increment_incrementauto_increment_offset 來實現 ID 自增。每次業務可使用下列 SQL 進行讀寫獲得 ID:算法

begin;
REPLACE INTO Tickets64 (stub) VALUES ('a');
SELECT LAST_INSERT_ID();
commit;
  • 優勢:使用很是簡單,ID 單調遞增。
  • 缺點:很是依賴數據庫,當數據庫異常時則整個系統不可用。

UUID

  • 優勢:本地生成,沒有網絡消耗,性能高。
  • 缺點:過長不易於存儲;形成信息不安全,基於 MAC 地址生成可能會形成 MAC 地址泄露。

Snowflake

Snowflake(雪花算法)是由 Twitter 發佈的分佈式 ID 生成算法,它可以保證不一樣進程主鍵的不重複性,以及相同進程主鍵的有序性。它是經過時間位實現單調遞增,且各個服務器若是都作了時間同步,那麼生成的 ID 能夠認爲是整體有序的。sql

Leaf

Leaf 最先期需求是各個業務線的訂單 ID 生成需求。在美團早期,有的業務直接經過數據庫自增的方式生成 ID,有的業務經過 Redis 緩存來生成 ID,也有的業務直接用 UUID 這種方式來生成 ID。以上的方式各自有各自的問題,所以決定實現一套分佈式 ID 生成服務來知足需求。docker

Leaf-segment

Leaf-segment 數據庫方案,在使用數據庫的方案上,作了如下改變:數據庫

  • 原方案每次獲取 ID 都得讀寫一次數據庫,形成數據庫壓力大。改成利用 proxy server 批量獲取一個 segment 號段,用完以後再去數據庫獲取新的號段,大大減輕數據庫的壓力。
  • 各個業務不一樣的發號需求用 biz_tag 字段來區分,每一個 big_tag 的 ID 獲取相互隔離互不影響。

優勢:api

  • Leaf 服務能夠很方便的線性擴展,性能徹底可以支撐大多數業務場景。
  • ID 是趨勢遞增的 8 字節的 64 位數字,知足數據庫存儲的主鍵要求。
  • 容災性高:Leaf 服務內部有號段緩存,即便數據庫宕機,短期內仍能能夠正常對外提供服務。
  • 能夠自定義 max_id 大小。

缺點:緩存

  • ID 不夠隨機,可以泄露發號數量的信息,不安全。
  • 數據庫宕機可能會形成整個系統不可用。
Leaf-snowflake

該方案徹底沿用 snowflake 方案設計。對於 workerID 的分配,當服務器集羣數量較小的狀況下,徹底能夠手動配置。服務規模較大時,動手配置成本過高,因此使用 Zookeeper 持久順序節點的特性自動對 snowflake 節點配置。安全

啓動步驟以下:服務器

  1. 啓動 Leaf-snowflake 服務,鏈接 Zookeeper,在 leaf_forever 父節點下檢查本身是否已經註冊過。
  2. 若是有註冊過直接取回本身的 workerID,啓動服務。
  3. 若是沒有註冊過,就在該父節點下面建立一個持久順序節點,建立成功後取回順序號當作本身的 workerID 號。

使用 Docker Compose 部署 Leaf

克隆項目

$ git clone https://github.com/antoniopeng/leaf.git
$ cd leaf
$ mvn clean install -DskipTests

構建

$ cd leaf-docker
$ chmod +x build.sh
$ ./build.sh

啓動

$ docker-compose up -d

測試

生成地址:http://localhost:8080/api/snowflake/get/test

$ curl http://localhost:8080/api/snowflake/get/test
相關文章
相關標籤/搜索