雪花算法的原理以及實現

文章目錄

  • 一、簡介
  • 二、算法優缺點
  • 三、算法實現

一、簡介

有這么一種說法,自然界中并不存在兩片完全一樣的雪花的。每一片雪花都擁有自己漂亮獨特的形狀、獨一無二。雪花算法也表示生成的ID如雪花般獨一無二。

雪花算法 (SnowFlake )算法,是 Twitter 開源的分布式 id 生成算法

核心思想是:使用一個 64 bit 的 long 型的數字作為全局唯一 id。在分布式系統中的應用十分廣泛,且ID 引入了時間戳,基本上保持自增的。

這 64 個 bit 中,其中 1 個 bit 是不用的(我們生成的 id 都是正數,所以第一個 bit 統一都是 0),然后用其中的 41 bit 作為毫秒數,用 10 bit 作為工作機器 id,12 bit 作為序列號。
雪花算法

第一個部分,是 1 個 bit:0,這個是無意義的。

第二個部分是 41 個 bit:表示的是時間戳。

第三個部分是 5 個 bit:表示的是機房 id,10001。

第四個部分是 5 個 bit:表示的是機器 id,1 1001。

第五個部分是 12 個 bit:表示的序號,就是某個機房某臺機器上這一毫秒內同時生成的 id 的序號,0000 00000000。

①1 bit:是不用的,為啥呢?

因為二進制里第一個 bit 為如果是 1,那么都是負數,但是我們生成的 id 都是正數,所以第一個 bit 統一都是 0。

②41 bit:表示的是時間戳,單位是毫秒。

41 bit 可以表示的數字多達 2^41 - 1,也就是可以標識 2 ^ 41 - 1 個毫秒值,換算成年就是表示 69 年的時間。

③10 bit:記錄工作機器 id,代表的是這個服務最多可以部署在 2^10 臺機器上,也就是 1024 臺機器。

但是 10 bit 里 5 個 bit 代表機房 id,5 個 bit 代表機器 id。意思就是最多代表 2 ^ 5 個機房(32 個機房),每個機房里可以代表 2 ^ 5 個機器(32 臺機器),也可以根據自己公司的實際情況確定。

④12 bit:這個是用來記錄同一個毫秒內產生的不同 id。

12 bit 可以代表的最大正整數是 2 ^ 12 - 1 = 4096,也就是說可以用這個 12 bit 代表的數字來區分同一個毫秒內的 4096 個不同的 id。

二、算法優缺點

雪花算法的優點:

(1)無依賴:不依賴第三方庫或者中間件,完全在內存中生成,可用性強。

(2)高性能:每秒中能生成數百萬的自增ID。

(3)ID自增:基于時間戳,以及同一時間戳下序列號自增,基本保證 id 有序遞增。

雪花算法的缺點:

依賴與系統時間的一致性,如果系統時間被回調,或者改變,可能會造成id沖突或者重復。算法中可通過記錄最后一個生成 id 時的時間戳來解決,每次生成 id 之前比較當前服務器時鐘是否被回撥,避免生成重復 id。

三、算法實現

代碼實現:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.NetworkInterface;public class IdWorker {private static final Logger LOGGER = LoggerFactory.getLogger(IdWorker .class);private final static String ERROR_CLOCK_BACK = "時間回撥,拒絕為超出%d毫秒生成ID";private final static String ERROR_ATTR_LIMIT = "%s屬性的范圍為0-%d";/*** 用于用當前時間戳減去這個時間戳,算出偏移量*/protected static final long TWEPOCH = 1538211907857L;/*** 機器id所占的位數(表示只允許workId的范圍為:0-1023)*/protected static final long WORKER_ID_BITS = 5L;/*** 數據標識id所占的位數*/protected static final long DATACENTER_ID_BITS = 5L;/*** 支持的最大機器id,結果是31 (這個移位算法可以很快的計算出幾位二進制數所能表示的最大十進制數)*/private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);/*** 支持的最大數據標識id,結果是31*/private static final long MAX_DATACENTER_ID = ~(-1L << DATACENTER_ID_BITS);/*** 序列在id中占的位數 (表示只允許sequenceId的范圍為:0-4095)*/protected static final long SEQUENCE_BITS = 12L;/*** 機器ID向左移12位*/private static final long WORKER_ID_SHIFT = SEQUENCE_BITS;/*** 數據標識id向左移17位(12+5)*/private static final long DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;/*** 時間截向左移22位(5+5+12)*/private static final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATACENTER_ID_BITS;/*** 生成序列的掩碼,(防止溢出:位與運算保證計算的結果范圍始終是 0-4095,0b111111111111=0xfff=4095)*/private static final long SEQUENCE_MASK = -1L ^ (-1L << SEQUENCE_BITS);/*** 工作機器ID(0~31)*/private long workerId;/*** 數據中心ID(0~31)*/private long datacenterId;/*** 毫秒內序列(0~4095)*/private long sequence = 0L;/*** 上次生成ID的時間截*/private long lastTimestamp = -1L;public IdWorker () {this.datacenterId = getDataCenterId(MAX_DATACENTER_ID);this.workerId = getWorkerId(datacenterId, MAX_WORKER_ID);}public IdWorker (long workerId, long dataCenterId) {if (workerId > MAX_WORKER_ID || workerId < 0) {throw new IllegalArgumentException(String.format(ERROR_ATTR_LIMIT, "workerId", MAX_WORKER_ID));}this.workerId = workerId;this.datacenterId = dataCenterId;}private static long getWorkerId (long dataCenterId, long maxWorkerId) {StringBuffer mpid = new StringBuffer();mpid.append(dataCenterId);String name = ManagementFactory.getRuntimeMXBean().getName();if (!name.isEmpty()) {// GET jvmPidmpid.append(name.split("@")[0]);}// MAC + PID 的 hashcode 獲取16個低位return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);}private static long getDataCenterId(long tempMaxDataCenterId) {if (tempMaxDataCenterId < 0L || tempMaxDataCenterId > MAX_DATACENTER_ID) {tempMaxDataCenterId = MAX_DATACENTER_ID;}long id = 0L;try {InetAddress ip = InetAddress.getLocalHost();NetworkInterface network = NetworkInterface.getByInetAddress(ip);if (network == null) {id = 1L;} else {byte[] mac = network.getHardwareAddress();id = ((0x000000FF & (long) mac[mac.length - 1])| (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;id = id % (tempMaxDataCenterId + 1);}} catch (Exception e) {LOGGER.warn("Get Data Center Id error, e:{}", e);}return id;}public synchronized long nextId() {long timestamp = timeGen();// 閏秒:如果當前時間小于上一次ID生成的時間戳,說明系統時鐘回退過這個時候應當拋出異常if (timestamp < lastTimestamp) {long offset = lastTimestamp - timestamp;if (offset <= 5) {try {// 時間偏差大小小于5ms,則等待兩倍時間wait(offset << 1);timestamp = timeGen();if (timestamp < lastTimestamp) {// 還是小于,拋異常并上報throw new RuntimeException(String.format(ERROR_CLOCK_BACK, lastTimestamp - timestamp));}} catch (InterruptedException e) {throw new RuntimeException(e);}} else {throw new RuntimeException(String.format(ERROR_CLOCK_BACK, lastTimestamp - timestamp));}}// 解決跨毫秒生成ID序列號始終為偶數的缺陷:如果是同一時間生成的,則進行毫秒內序列if (lastTimestamp == timestamp) {// 通過位與運算保證計算的結果范圍始終是 0-4095sequence = (sequence + 1) & SEQUENCE_MASK;// 毫秒內序列溢出if (sequence == 0) {// 阻塞到下一個毫秒,獲得新的時間戳timestamp = tilNextMillis(lastTimestamp);}} else {// 時間戳改變,毫秒內序列重置sequence = 0L;}// 上次生成ID的時間截lastTimestamp = timestamp;return ((timestamp - TWEPOCH) << TIMESTAMP_LEFT_SHIFT)| (datacenterId << DATACENTER_ID_SHIFT)| (workerId << WORKER_ID_SHIFT)| sequence;}private long tilNextMillis(long lastTimestamp) {long timestamp = timeGen();while (timestamp <= lastTimestamp) {timestamp = timeGen();}return timestamp;}private long timeGen() {return System.currentTimeMillis();}
}

測試代碼:

public static void main(String[] args) {IdWorker idWorker = new IdWorker();for (int i = 0; i < 10; i++){System.out.println(idWorker.nextId());}}

輸出結果:

761722546083581952
761722546083581953
761722546083581954
761722546087776256
761722546087776257
761722546087776258
761722546087776259
761722546087776260
761722546087776261
761722546087776262

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/38680.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/38680.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/38680.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

幾度互聯網站群管理系統全媒體解決方案

隨著高考的結束&#xff0c;各高校開啟了緊張的招生宣傳工作&#xff0c;幾度互聯網站群系統助力各高校招生宣傳。 學校官方網站是互聯網時代學校對外交流的重要途徑和信息公開的主要載體&#xff0c;是展示學校形象、密切聯系師生的重要窗口&#xff0c;是加強校園宣傳思想工…

【MySQL備份】Percona XtraBackup篇

目錄 1.關于Percona XtraBackup 2. Percona XtraBackup有哪些特點&#xff1f; 3.安裝Percona XtraBackup 3.1.環境信息 3.2.安裝步驟 4.實戰演練 4.1.全量備份與恢復 4.2.總結 1.關于Percona XtraBackup Percona XtraBackup是世界上唯一的開源、免費的MySQL熱備份 為…

品牌推廣方案怎么寫?策劃書模板與實戰技巧分享

品牌想要快速得到市場的認可&#xff0c;一個精心策劃的品牌推廣方案是脫穎而出的關鍵。 作為一名手工酸奶品牌創始人&#xff0c;目前全國也復制了100多家門店&#xff0c;這篇文章&#xff0c;我和大家分享下&#xff0c;如何做一個清晰的結構框架、策劃書模板以及實戰技巧&…

【論文閱讀】-- TimeNotes:時間序列數據的有效圖表可視化和交互技術研究

TimeNotes: A Study on Effective Chart Visualization and Interaction Techniques for Time-Series Data 摘要1 介紹和動機2 文獻2.1 時間序列數據探索2.1.1 數據聚合2.1.2 基于透鏡2.1.3 基于布局 3 任務和設計3.1 數據3.2 領域表征3.3 探索、分析和呈現 4 TimeNotes4.1 布局…

Kaggle競賽——房價預測

目錄 1. 特征分析1.1 數據集導入1.2 統計缺失值1.3 可視化缺失值1.4 缺失值相關性分析1.5 訓練集和測試集缺失數據對比1.6 統計特征的數據類型1.7 數值型特征分布直方圖1.8 數值型特征與房價的線性關系1.9 非數值型特征的分布直方圖1.10 非數值型特征箱線圖1.11 數值型特征填充…

JAVA:常用的算法指南

請關注微信公眾號&#xff1a;拾荒的小海螺 博客地址&#xff1a;http://lsk-ww.cn/ 1、簡述 在軟件開發過程中&#xff0c;算法扮演著關鍵的角色。它們用于解決各種問題&#xff0c;從數據處理到搜索、排序等。本文將介紹幾種常見的算法及其 Java 實現&#xff0c;包括排序算…

ffmpeg推流時Unknown encoder ‘libx264‘

如果環境中有conda&#xff0c;最簡單的辦法就是 conda uninstall ffmpeg conda install ffmpeg 或者 sudo apt-get install -y libgmp3-dev pkg-config gnutls-bin libaom-dev libass-dev libbluray-dev libfdk-aac-dev libmp3lame-dev libopencore-amrnb-dev libopencore-…

基于java+springboot+vue實現的農產品直賣平臺(文末源碼+Lw)266

摘 要 計算機網絡發展到現在已經好幾十年了&#xff0c;在理論上面已經有了很豐富的基礎&#xff0c;并且在現實生活中也到處都在使用&#xff0c;可以說&#xff0c;經過幾十年的發展&#xff0c;互聯網技術已經把地域信息的隔閡給消除了&#xff0c;讓整個世界都可以即時通…

Python從0到100(三十三):xpath和lxml類庫

1. 為什么要學習xpath和lxml lxml是一款高性能的 Python HTML/XML 解析器&#xff0c;我們可以利用XPath&#xff0c;來快速的定位特定元素以及獲取節點信息 2. 什么是xpath XPath&#xff0c;全稱為XML Path Language&#xff0c;是一種用于在XML文檔中進行導航和數據提取的…

Python基礎之多進程

文章目錄 1 多進程1.1 簡介1.2 Linux下多進程1.3 multiprocessing1.4 Pool1.5 進程間通信1.6 分布式進程 1 多進程 1.1 簡介 要讓Python程序實現多進程&#xff08;multiprocessing&#xff09;&#xff0c;我們先了解操作系統的相關知識。 Unix/Linux操作系統提供了一個fork…

豆包文科成績超了一本線,為什么理科不行?

卡奧斯智能交互引擎是卡奧斯基于海爾近40年工業生產經驗積累和卡奧斯7年工業互聯網平臺建設的最佳實踐&#xff0c;基于大語言模型和RAG技術&#xff0c;集合海量工業領域生態資源方優質產品和知識服務&#xff0c;旨在通過智能搜索、連續交互&#xff0c;實時生成個性化的內容…

使用Java構建可擴展的微服務架構

使用Java構建可擴展的微服務架構 大家好&#xff0c;我是免費搭建查券返利機器人省錢賺傭金就用微賺淘客系統3.0的小編&#xff0c;也是冬天不穿秋褲&#xff0c;天冷也要風度的程序猿&#xff01;今天我們將探討如何使用Java構建可擴展的微服務架構&#xff0c;這是現代軟件開…

Java - 程序員面試筆記記錄 實現 - Part2

2.1 輸入輸出流 流可以被看作一組有序的字節集合&#xff0c;即數據在兩個設備間的傳輸。 字節流&#xff1a;以字節作為單位&#xff0c;讀到一個字節就返回一個字節&#xff1b;InputStream & OutputStream。 字符流&#xff1a;使用字節流讀到一個到多個字節先查詢碼…

【Invalid mapping pattern】SpringMVC路徑匹配

報錯&#xff1a; Description:Invalid mapping pattern detected: /**/{[path:[^.]] ^ No more pattern data allowed after {...} or ** pattern elementAction:Fix this pattern in your application or switch to the legacy parser implementation with spring.mvc.pathm…

VLC for Unity播放RTSP延遲高的解決辦法

VLC for Unity播放RTSP延遲高的解決辦法&#xff1a; 設置網絡緩存時長network-caching100 public void Open(){Log("VLCPlayerExample Open");if (mediaPlayer.Media ! null)mediaPlayer.Media.Dispose();List<string> options new List<string>();o…

Eureka在微服務架構中的服務降級策略解析

引言 微服務架構因其靈活性和可擴展性而受到現代軟件開發的青睞。然而&#xff0c;隨著服務數量的增加&#xff0c;系統的復雜性也隨之上升&#xff0c;服務間的依賴關系可能導致單點故障&#xff0c;影響整個系統的穩定性。服務降級是一種常見的應對策略&#xff0c;用于在服…

基于RabbitMQ的異步消息傳遞:發送與消費

引言 RabbitMQ是一個流行的開源消息代理&#xff0c;用于在分布式系統中實現異步消息傳遞。它基于Erlang語言編寫&#xff0c;具有高可用性和可伸縮性。在本文中&#xff0c;我們將探討如何在Python中使用RabbitMQ進行消息發送和消費。 安裝RabbitMQ 在 Ubuntu 上安裝 Rabbi…

提升寫作效率:探索AI在現代辦公自動化中的應用

工欲善其事&#xff0c;必先利其器。 隨著AI技術與各個行業或細分場景的深度融合&#xff0c;日常工作可使用的AI工具呈現出井噴式發展的趨勢&#xff0c;AI工具的類別也從最初的AI文本生成、AI繪畫工具&#xff0c;逐漸擴展到AI思維導圖工具、AI流程圖工具、AI生成PPT工具、AI…

精通SQL Server端口管理:添加與刪除監聽端口的指南

引言 SQL Server的端口管理是數據庫管理員(DBA)必須掌握的關鍵技能之一。端口配置不僅關系到數據庫的網絡通信能力&#xff0c;還直接影響到數據庫的安全性和性能。本文將詳細介紹如何在SQL Server中添加和刪除監聽端口&#xff0c;以及相關的配置策略和最佳實踐。 SQL Serve…

ubuntu 系統中 使用docker 制作 Windows 系統,從此告別 vmware虛擬機

我的系統是 ubuntu 24 前期準備工作&#xff1a; 安裝dockerdocker pull 或者 手動制作鏡像 docker build 的話 必須要 科學上網&#xff0c; 好像阿里鏡像都下不下來。需要 知道 docker 和docker compose 命令的使用方式 我是給docker 掛了 http代理 如果你能pull下來鏡像 …