在幾乎所有的分布式系統或者採用了分庫/分表設計的系統中,幾乎都會需要生成資料的唯一標識id的需求,
常規做法,是使用資料庫中的自動增長列來做系統主鍵,但是這樣的做法無法保證id全域性唯一.
那麼乙個分布式id生成器應該滿足那些需求呢 :
本文將基於snowflake的演算法來進行以下的討論,當然,分布式id的生成方案有很多,
先看兩段**:
public
void
id()
log.info("{} , {}"
, maxcount, map.size());
}
輸出為 : 100 , 100
public
void
id()
log.info("{} , {}"
, maxcount, map.size());
}
輸出為 : 100 , 10
了解snowflake的同學也都知道,這個演算法是基於時間的,如下組成 :
0 | 時間(41位) | 資料中心id(5位) | 機器id(5位) | 序號(12位)
而生成id的演算法邏輯,簡單點說,在相同資料中心id和機器id的情況下,如果時間的毫秒數是一致的,那麼就通過遞增序列號來保證id不重複.
也就是說在1毫秒內最大生成的id個數是二進位制12bit的最大值,也就是4096(0-4095)個
那麼如果序列號超過了這個最大值,則會將程式阻塞到下一毫秒,然後序列號歸零,繼續生成id.
好知道了生成id的邏輯後,上面兩個程式判斷的現象也就不難解釋了.
程式一 : 沒有重複,是因為在整個迴圈中,id生成器只例項化過一次,在迴圈的過程中,能正常的遞增序列號,所以不會有重複的id出現
程式二 : 有重複,是因為id生成器是在迴圈中迴圈例項化的,每次生成id的時候序列號都是0,但是程式執行很快,得到的時間毫秒數又是一樣的,那麼,就必然會有重複值了.
所以從以上的程式片段和分析中可以得出乙個結論 : 要想snowflake生成全域性唯一的id,那麼id生成器必須也是全域性單例的
兩個點要主注意一下 :
所以得出乙個結論 : snowflake可以通過datacenterid和workerid來區分id的歸屬(可以是業務線,可以是機房,等等,按需定義)來達到更大的id生成數量
所以大家先看一下架構圖 :
#生成分布式id(按時間戳區分datacenterid和workerid)
/service/id
#生成分布式id(按dwid[0-1023
])/service/id/
#生成分布式id(按datacenterid[0-31
]和workerid[0-31
])/service/id//
#批量生成分布式id(按時間戳區分datacenterid和workerid)
/service/id/batch/
#批量生成分布式id(按dwid[0-1023
])/service/id/batch//
#批量生成分布式id(按datacenterid[0-31
]和workerid[0-31
])/service/id/batch///
在提供出來的rest服務中,提供了datacenterid和workerid的引數(dwid就是兩者的融合,10bit),
總共預留了10個bit的空餘來支援分庫分表,最大支援1024個節點.
反解析分布式id
snowflake生成的id是可以被反解析的,這樣更進一步的支援了分庫的相關炒作,相關實現如下 :
id reverseid = new
id();
reverseid.setsequence((id) & ~(-1l
<< 12)); // sequence
reverseid.setdwid((id >> (12)) & ~(-1l
<< (10))); // dwid
reverseid.setworkerid((id >> 12) & ~(-1l
<< 5)); // workerid
reverseid.setdatacenterid((id >> 17) & ~(-1l
<< 5)); // datacenterid
reverseid.settimestamp((id >> 22) + twepoch); // timestamp
return
reverseid;
本方案是可以支援id生成服務有多個例項,最多1024個,能並且能保證每個例項內,相同datacenterid和workerid的id生成器只有乙個,做到全域性單例.
主要是通過redis原子鎖的來實現的.詳情可看上面的流程圖,主要分為本地id生成和跨例項id生成兩種模式 :
本地生成
這種情況比較簡單,就是生成id的請求剛剛落到id生成器所在的例項上,然後就可以直接拿到id生成器,然後生成id.
跨例項id生成
這種情況簡單點說就是,比如你要生成3-3的id,這個id生成器在例項a上,但是負載均衡器將請求發到例項b上去了,
這個時候例項b上並沒有對應的id生成器,這個時候,就會從快取中拿到對應的快取值,拿到用用這個id生成器的host和port,
然後在做乙個rms請求,呼叫遠端的rest服務,生成id,然後返回
上面提到了,id生成器現在是全網單例的了,那麼其中乙個節點有故障,掛掉了怎麼辦呢?
在跨例項id生成的場景下,會有rms請求失敗的情況,遠端節點有可能會故障,這個時候,一旦rms請求失敗,則會觸發故障轉移,
具體操作就是將redis中的對應快取刪除掉,然後走乙個例項化id生成器的流程,這個時候,當前處理請求的節點就會將故障節點擁有的id生成器轉移過來,轉為本地生成模式,從而做到的故障轉移
如果是本地id生成的話,那基本沒有效能損耗,直接操作本地變數.
跨例項id生成的情況會多出來乙個rms請求的耗時,但是一次id生成的請求最多觸發一次rms請求,消耗是可控的
在有節點故障的時候,觸發故障轉移會額外的產生一次id例項化的流程,會造成輕微波動,但緊當前的這一次請求,下次的請求就會轉為本地id生成的模式
今天跟大家分享了如何動態分配snowflake的datacenterid和workerid,以及如何做到高可用的設計和思路,環境大家提出意見和建議
分布式唯一ID生成服務
snservice是一款基於分布式的唯一id生成服務,主要用於提供大數量業務資料建立唯一id的需要 服務提供最低10k s的唯一id請求處理.如果你部署服務的cpu資源達到4核的情況下那該服務最低可以提供100k s的請求處理能力.服務支援部署到linux mono 3.2.3和windows ne...
分布式ID生成器
一 需求緣起 幾乎所有的業務系統,都有生成乙個唯一記錄標識的需求,例如 這個記錄標識往往就是資料庫中的主鍵,資料庫上會建立聚集索引 cluster index 即在物理儲存上以這個字段排序。這個記錄標識上的查詢,往往又有分頁或者排序的業務需求,例如 所以往往要有乙個time欄位,並且在time欄位上...
分布式id生成系統 總結
簡單易用,但是做資料庫分片的時候,uuid不太適合作為分片鍵 詳見leaf 美團點評分布式id生成系統 效能非常高,缺點是如果時間回撥或者各個例項節點時間不一致,容易出錯 詳見leaf 美團點評分布式id生成系統 支援多種不同模式的生成策略 號段模式 該模式需要建db表,需要有專門的服務來提供獲取i...