1. 程式人生 > >分散式 ID 解決方案之美團 Leaf

分散式 ID 解決方案之美團 Leaf

### 分散式 ID 在龐大複雜的分散式系統中,通常需要對海量資料進行唯一標識,隨著資料日漸增長,對資料分庫分表以後需要有一個唯一 ID 來標識一條資料,而資料庫的自增 ID 顯然不能滿足需求,此時就需要有一個能夠生成全域性唯一 ID 的系統,需要滿足以下條件: - 全域性唯一性:最基本的要求就是不能出現重複的 ID。 - 遞增:保證下一個 ID 一定大於上一個 ID。 - 資訊保安:如果 ID 是連續的,使用者就可以按照順序進行惡意爬取資料,所以 ID 生成無規則。 > 上述的 2 和 3 點需求是互斥的,無法使用同一個方案滿足。 ### 解決方案 #### 資料庫生成 以 MySQL 為例,利用給欄位設定 `auto_increment_increment` 和 `auto_increment_offset` 來實現 ID 自增。每次業務可以使用下列 SQL 進行讀寫得到 ID: ```sql 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` 節點配置。 啟動步驟如下: 1. 啟動 `Leaf-snowflake` 服務,連線 `Zookeeper`,在 `leaf_forever` 父節點下檢查自己是否已經註冊過。 2. 如果有註冊過直接取回自己的 `workerID`,啟動服務。 3. 如果沒有註冊過,就在該父節點下面建立一個持久順序節點,建立成功後取回順序號當做自己的 `workerID` 號。 ### 使用 Docker Compose 部署 Leaf #### 克隆專案 ```sh $ git clone https://github.com/antoniopeng/leaf.git ``` ```sh $ cd leaf ``` ```sh $ mvn clean install -DskipTests ``` #### 構建 ```sh $ cd leaf-docker ``` ```sh $ chmod +x build.sh ``` ```sh $ ./build.sh ``` #### 啟動 ```sh $ docker-compose up -d ``` #### 測試 生成地址:http://localhost:8080/api/snowflake/get/test ```sh $ curl http://localhost:8080/api/snowflake/get/test ``` > - 文章作者:彭超 > > - 本文首發於個人部落格:[https://antoniopeng.com/2020/07/22/%E5%88%86%E5%B8%83%E5%BC%8F/%E5%88%86%E5%B8%83%E5%BC%8F%20ID%20%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88%E4%B9%8B%E7%BE%8E%E5%9B%A2%20Leaf/](https://antoniopeng.com/2020/07/22/%E5%88%86%E5%B8%83%E5%BC%8F/%E5%88%86%E5%B8%83%E5%BC%8F%20ID%20%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88%E4%B9%8B%E7%BE%8E%E5%9B%A2%20Leaf/) > > - 版權宣告:本部落格所有文章除特別宣告外,均採用 CC BY-NC-SA 4.0 許可協議。轉載請註明來自 [彭超 | Blog](https://link.zhihu.com/?target=https%3A//antoniopeng.