分佈式唯一ID服務架構

一、背景介紹

在大型互聯網應用中,隨着用戶數的增加;爲了提高應用的性能,我們經常需要對數據庫進行分庫分表操作。在單表時代我們可以完全依賴於數據庫的自增ID來唯一標識一個條數據。但是當我們對數據庫進行了分庫分表之後,就不能依賴於每個表的自增ID來全局唯一標識這些數據了。因爲自增的ID不能在分庫分表的場景下準確的路由到正確的數據。
因此我們需要提供一個全局唯一的ID生成策略來支持分庫分表的應用環境;
這個系統必須滿足以下需求:
· 全局唯一: 不能出現重複的ID;
· 高可用: ID生成系統屬於基礎服務,同時被許多關鍵系統調用,一旦宕機,會造成嚴重影響;

二、經典方案介紹

1. UUID
UUID是Universally Unique Identifier的縮寫,它是在一定範圍內(從特定的名字空間到全球)唯一的機器生成的標識符,UUID是16字節128位長的數字,通常以36字節的字符串表示;比如:
UUID經由一定的算法機器生成,爲了保證UUID的唯一性,規範定義了包括網卡MAC地址、時間戳、名字空間(Namespace)、隨機或僞隨機數、時序等元素,以及從這些元素生成UUID的算法。UUID的複雜特性在保證了其唯一性的同時,意味着只能由計算機生成。
優點:本地生成ID,不需要遠程調用、低延時、性能高;
缺點:UUID過長,16字節128位,很多場景不適用;比如用UUID做數據庫的索引時,插入數據時數據量越大,插入性能越低;
UUID不是有序的,無法保證趨勢遞增;
2. Flicker方案
該方案主要的思路是採用了MySQL自增長的ID的機制(auto_increment + replace into)

--- 數據表CREATE TABLE Tickets64 (
  id     bigint(20) unsigned NOT NULL auto_increment,
  stub char(1)     NOT NULL                default '',
  PRIMARY KEY (id),
  UNIQUE KEY   stub (stub))ENGINE=MyISAM;
--- 每次業務使用下列sql讀寫MySQL得到ID號
REPLACE INTO Tickets64 (stub) VALUES ('a');
SELECT LAST_INSERT_ID();

replace into跟insert功能類似,不同之處在於: replace into 首先嚐試插入數據到表中,如果發現表中已經有此行數據則先刪除此行數據,然後插入新的數據,否則直接插入新數據;
優點:充分藉助數據庫的自增ID機制,可靠性高,生成有序ID
缺點:ID生成性能依賴單臺數據庫讀寫性能;
依賴數據庫,當數據庫異常時整個系統不可用。
3. Twitter-Snowflake方案
Twitter-Snowflake算法產生的背景相當簡單,是爲了滿足Twitter每秒上萬條消息的請求,每條消息都必須分配一條唯一的id,這些id還需要一些大致的順序(方便客戶排序),並且在分佈式系統中不同機器產生的id必須不同。
Snowflake算法核心
把時間戳、工作機器Id、序列號組合在一起。
【加羣】:857565362
除了最高位bit標記爲不可用以外,其餘三組bit佔位均可浮動,具體看業務需求而定。默認情況下:
41bit的時間戳可以支持該算法使用到2082年;
10bit的工作機器id可以支持1023臺機器,
序列號支持1毫秒產生4095個自增序列id。
Snowflake - 時間戳
在這裏,時間戳的粒度爲毫秒級,具體代碼如下:

uint64_t generateStamp() {
  timeval tv;
  gettimeofday(&tv, 0);
  return (uint64_t)tv.tv_sec * 1000 + (uint64_t)tv.tv_usec / 1000;
}

默認情況下有41個bit可以使用,那麼(1 << 41) / (3600 * 24 * 365 * 1000) = 69.7年
Snowflake - 工作機器Id
嚴格意義來說工作機器Id可以是進程級的, 機器級的話可以使用MAC地址來唯一標示工作機器,工作進程級可以使用IP + Path來區分工作進程。如果工作機器比較少,可以使用配置文件來設置這個id是一個不錯的選擇,如果機器過多配置文件來維護則是一件災難性的事情。
Snowflake - 序列號
序列號就是一系列的自增Id,爲了處理在同一毫秒內需要給多條消息分配id,若同一毫秒把序列號用完了,則“等待至下一毫秒”

uint64_t waitNextMs(uint64_t lastStamp){
  uint64_t cur = 0;
  do {
    cur = generateStamp();
  } while (cur <= lastStamp);
  return cur;
}

我這兒整理了比較全面的JAVA相關的面試資料,
需要領取面試資料的同學,請加羣:473984645
在這裏插入圖片描述
獲取更多學習資料,可以加羣:473984645或掃描下方二維碼
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章