分佈式 ID
在龐大複雜的分佈式系統中,通常需要對海量數據進行唯一標識,隨着數據日漸增長,對數據分庫分表以後需要有一個唯一 ID 來標識一條數據,而數據庫的自增 ID 顯然不能滿足需求,此時就需要有一個能夠生成全局唯一 ID 的系統,需要滿足以下條件:
- 全局唯一性:最基本的要求就是不能出現重複的 ID。
- 遞增:保證下一個 ID 一定大於上一個 ID。
- 信息安全:如果 ID 是連續的,用戶就可以按照順序進行惡意爬取數據,所以 ID 生成無規則。
上述的 2 和 3 點需求是互斥的,無法使用同一個方案滿足。
生成方案
數據庫生成
以 MySQL 爲例,利用給字段設置 auto_increment_increment
和 auto_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 可以認爲是總體有序的。
Leaf
Leaf
最早期需求是各個業務線的訂單 ID 生成需求。在美團早期,有的業務直接通過數據庫自增的方式生成 ID,有的業務通過 Redis 緩存來生成 ID,也有的業務直接用 UUID 這種方式來生成 ID。以上的方式各自有各自的問題,因此決定實現一套分佈式 ID 生成服務來滿足需求。
Leaf-segment
Leaf-segment
數據庫方案,在使用數據庫的方案上,做了以下改變:
- 原方案每次獲取 ID 都得讀寫一次數據庫,造成數據庫壓力大。改爲利用 proxy server 批量獲取一個 segment 號段,用完之後再去數據庫獲取新的號段,大大減輕數據庫的壓力。
- 各個業務不同的發號需求用 biz_tag 字段來區分,每個 big_tag 的 ID 獲取相互隔離互不影響。
優點:
- Leaf 服務可以很方便的線性擴展,性能完全能夠支撐大多數業務場景。
- ID 是趨勢遞增的 8 字節的 64 位數字,滿足數據庫存儲的主鍵要求。
- 容災性高:Leaf 服務內部有號段緩存,即使數據庫宕機,短時間內仍能可以正常對外提供服務。
- 可以自定義 max_id 大小。
缺點:
- ID 不夠隨機,能夠泄露發號數量的信息,不安全。
- 數據庫宕機可能會造成整個系統不可用。
Leaf-snowflake
該方案完全沿用 snowflake
方案設計。對於 workerID
的分配,當服務器集羣數量較小的情況下,完全可以手動配置。服務規模較大時,動手配置成本太高,所以使用 Zookeeper
持久順序節點的特性自動對 snowflake
節點配置。
啓動步驟如下:
- 啓動
Leaf-snowflake
服務,連接Zookeeper
,在leaf_forever
父節點下檢查自己是否已經註冊過。 - 如果有註冊過直接取回自己的
workerID
,啓動服務。 - 如果沒有註冊過,就在該父節點下面創建一個持久順序節點,創建成功後取回順序號當做自己的
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
文章作者:彭超
版權聲明:本博客所有文章除特別聲明外,均採用 CC BY-NC-SA 4.0 許可協議。轉載請註明來自 彭超的博客 | Antonio Blog