ID生成策略——SnowFlake
一、遇到問題
某個專案採用了資料庫(MySQL)自增ID作為主要業務資料的主鍵。資料庫自增ID使用簡單,自動編號,速度快,而且是增量增長,按順序存放,對於檢索非常有利。
單庫環境下,資料庫自增ID問題不大。但在分散式環境或分庫分表環境下,資料庫自增ID逐漸暴露出一些問題。例如,分庫分表的情況下保證ID唯一變得困難;訂單號等業務資料如果用資料庫自增ID,競對很容易算出大概的業務量。
二、常見的ID生成策略
1、資料庫自增ID(前面提到了)
2、UUID
演算法的核心思想是結合機器的網路卡、當地時間、一個隨記數來生成UUID。
優點:本地生成,生成簡單,效能好,沒有高可用風險
缺點:長度過長,儲存冗餘,且無序不可讀,查詢效率低
3、Redis生成ID
Redis生成ID可以看做資料庫自增ID的升級版。Redis的所有命令操作都是單執行緒的,本身提供像 incr 和 increby 這樣的自增原子命令,所以能保證生成的 ID 肯定是唯一有序的。
優點:不依賴於資料庫,靈活方便,且效能優於資料庫;數字ID天然排序,對分頁或者需要排序的結果很有幫助。
缺點:如果系統中沒有Redis,還需要引入新的元件,增加系統複雜度;需要編碼和配置的工作量比較大。
考慮到單節點的效能瓶頸,可以使用 Redis 叢集來獲取更高的吞吐量。假如一個叢集中有5臺 Redis。可以初始化每臺 Redis 的值分別是1, 2, 3, 4, 5,然後步長都是 5。各個 Redis 生成的 ID 為
4、Twitter的snowflake演算法。
三、snowflake演算法
snowflake演算法,採用64位二進位制整數。二進位制具體位數含義如下圖。
1位,不用。二進位制中最高位為1的都是負數,但是我們生成的id都使用正數,所以這個最高位固定是0
41位,用來記錄時間戳(毫秒)。
如果只用來表示正整數(計算機中正數包含0),可以表示的數值範圍是:0 至 241−1,減1是因為可表示的數值範圍是從0開始算的,而不是1。
也就是說41位可以表示241−1個毫秒的值,轉化成單位年則是(241−1)/(1000∗60∗60∗24∗365)=69年
10位,用來記錄工作機器id。
可以部署在1024個節點,包括5位datacenterId和5位workerId
12位,序列號,用來記錄同毫秒內產生的不同id。
12位(bit)可以表示的最大正整數是4095,即可以用0、1、2、3、....4095這4096個數字,來表示同一機器同一時間截(毫秒)內產生的4096個ID序號
大多數人都知道這個演算法,但Twitter 利用 zookeeper 還做了很多工程上的實現,感興趣可以看
擷取git上該工程的主要檔案目錄,
git工程README.md檔案中有這麼一段話
We have retired the initial release of Snowflake and working on open sourcing the next version based on Twitter-server, in a form that can run anywhere without requiring Twitter's own infrastructure services.
Twitter幾年前就停止了對這個專案的維護,新的版本也沒見著放出來。好在現有版本的核心演算法已經能夠滿足常規的需求。
當然,snowflake有眾多優點的同時也是有缺點的。
優點:
毫秒數在高位,自增序列在低位,整個ID都是趨勢遞增的。
不依賴資料庫等第三方系統,以服務的方式部署,穩定性更高,生成ID的效能也是非常高的。
可以根據自身業務特性分配bit位,非常靈活。
缺點:
強依賴機器時鐘,如果機器上時鐘回撥,會導致發號重複或者服務會處於不可用狀態。
強依賴時鐘在有些情況下很致命,我個人就遇到過伺服器剛重啟的短時間內時間沒有同步,造成生成ID出問題的情況!
四、一些改進策略
1、美團Leaf比較完美的方案
美團Leaf比較好的解決了這些問題,參看《Leaf——來自美團點評的分散式ID生成系統》
美團Leaf的方案核心有兩點
(1)依靠zookeeper實現workerId的自動化租用
(2)透過演算法解決了時鐘回撥問題
美團Leaf目前是開源軟體,可以在下載
2、一個候選人不嚴謹但成本很低的實現
我在面試中,一個候選人提出的方法也比較有意思(儘管這個方法不嚴謹)。
在redis中設定一個整數變數workerNum,初始值為0,snowflake id生成客戶端每次啟動時讀取redis中的變數,用workerNum%1024作為worker的值,然後把redis中的workerNum+1。
在idworker數量不多的情況下,這個方案一般不會出現workerId重複(因為隨著業務的迭代,一般情況下idworker過一段時間都會因為業務部署而重啟)。如果研發資源特別有限,又想使用snowflake可以考慮一下這個辦法。
3、個人專案中hash分庫的解決辦法
實際使用中,有時候ID需要支援分庫分表,snowflake的預設實現對這塊支援得不夠。在業務量不大的情況下,snowflake生成的id序列號部分大多都是0,轉換為十進位制會是偶數。用這個id透過取模hash分庫,顯然不平均。
萬一有這樣的需求怎麼辦呢?可以考慮藉助ID時間戳部分實現均勻分佈
(1)分庫分表邏輯使用ID中時間戳部分做取模。這個方法需要把10進位制ID轉成2進位制,然後移位,再進行計算。比較麻煩
(2)生成ID的時候把序列號部分尾數用時間戳對應的位置覆蓋。截段程式碼,這段程式碼的取值能保證ID除以128的餘數均勻分佈。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31556438/viewspace-2636137/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Snowflake 全域性唯一Id 生成
- 分散式 ID 生成演算法 — SnowFlake分散式演算法
- PHP 實現 Snowflake 生成分散式唯一 IDPHP分散式
- 分散式唯一id生成策略分散式
- Java 實現的SnowFlake生成UUID (Java程式碼實戰-007)JavaUI
- 資料庫主鍵 ID 生成策略資料庫
- 微服務之唯一ID生成策略微服務
- Shell指令碼實現Twitter的Snowflake演算法的ID生成器指令碼演算法
- 分散式的環境中id生成策略分散式
- 分散式唯一ID生成方案選型!詳細解析雪花演算法Snowflake分散式演算法
- 圖解Janusgraph系列-分散式id生成策略分析圖解分散式
- 全域性唯一ID生成常見的幾種方式和twitter/snowflake(雪花演算法)解析演算法
- 忘掉 Snowflake,感受一下效能高出 587 倍的全域性唯一 ID 生成演算法演算法
- 分散式唯一id:snowflake演算法思考分散式演算法
- Twitter的分散式雪花演算法 SnowFlake 每秒自增生成26個萬個可排序的ID (Java版)分散式演算法排序Java
- hibernate主鍵生成策略
- 使Snowflake的客戶能夠通過Snowflake平臺
- 唯一邀請碼生成策略
- Hibernate 主鍵的生成策略
- Twitter的分散式自增ID演算法snowflake (Java版)分散式演算法Java
- Hibernate框架的主鍵生成策略框架
- Android 分包策略Android
- 分散式ID系列(5)——Twitter的雪法演算法Snowflake適合做分散式ID嗎分散式演算法
- Java Hibernate 主鍵生成10大策略Java
- 生成changeID
- 分散式系統全域性唯一Id(SnowFlake)雪花演算法實現分散式演算法
- RAID 儲存策略AI
- 生成樹欺騙攻擊與防禦策略
- 【Java】生成UUIDJavaUI
- oracle生成uuidOracleUI
- 如何使用 IdGen 生成 UIDUI
- [Android]Okhttp心跳策略研究AndroidHTTP
- Android Oreo 後臺策略Android
- uuid自動生成UI
- 使用Flex生成GUIDFlexGUI
- 批次生成changeid
- Java開發學習(四十六)----MyBatisPlus新增語句之id生成策略控制及其簡化配置JavaMyBatis
- 資料雲時代,中國的“Snowflake”如何破局?