分散式主鍵解決方案之--Snowflake雪花演算法

来源:https://www.cnblogs.com/yanghj/archive/2019/10/07/11632811.html
-Advertisement-
Play Games

0--前言 對於分散式系統環境,主鍵ID的設計很關鍵,什麼自增intID那些是絕對不用的,比較早的時候,大部分系統都用UUID/GUID來作為主鍵,優點是方便又能解決問題,缺點是插入時因為UUID/GUID的不規則導致每插入一條數據就需要重新排列一次,性能低下;也有人提出用UUID/GUID轉lon ...


0--前言

  對於分散式系統環境,主鍵ID的設計很關鍵,什麼自增intID那些是絕對不用的,比較早的時候,大部分系統都用UUID/GUID來作為主鍵優點是方便又能解決問題,缺點是插入時因為UUID/GUID的不規則導致每插入一條數據就需要重新排列一次,性能低下;也有人提出用UUID/GUID轉long的方式,可以很明確的告訴你,這種方式long不能保證唯一,大併發下會有重覆long出現,所以也不可取,這個主鍵設計問題曾經是很多公司系統設計的一個頭疼點,所以大部分公司願意犧牲一部分性能而直接採用簡單粗暴的UUID/GUID來作為分散式系統的主鍵;

  twitter開源了一個snowflake演算法,俗稱雪花演算法;就是為瞭解決分散式環境下生成不同ID的問題;該演算法會生成19位的long型有序數字,MySQL中用bigint來存儲(bigint長度為20位);該演算法應該是目前分散式環境中主鍵ID最好的解決方案之一了;

1--snowflake雪花演算法實現

  好,廢話不多說,直接上演算法實現

  1 package com.anson;
  2 
  3 import java.lang.management.ManagementFactory;
  4 import java.net.InetAddress;
  5 import java.net.NetworkInterface;
  6 
  7 //雪花演算法代碼實現
  8 public class IdWorker {
  9     // 時間起始標記點,作為基準,一般取系統的最近時間(一旦確定不能變動)
 10     private final static long twepoch = 1288834974657L;
 11     // 機器標識位數
 12     private final static long workerIdBits = 5L;
 13     // 數據中心標識位數
 14     private final static long datacenterIdBits = 5L;
 15     // 機器ID最大值
 16     private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);
 17     // 數據中心ID最大值
 18     private final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
 19     // 毫秒內自增位
 20     private final static long sequenceBits = 12L;
 21     // 機器ID偏左移12位
 22     private final static long workerIdShift = sequenceBits;
 23     // 數據中心ID左移17位
 24     private final static long datacenterIdShift = sequenceBits + workerIdBits;
 25     // 時間毫秒左移22位
 26     private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
 27 
 28     private final static long sequenceMask = -1L ^ (-1L << sequenceBits);
 29     /* 上次生產id時間戳 */
 30     private static long lastTimestamp = -1L;
 31     // 0,併發控制
 32     private long sequence = 0L;
 33 
 34     private final long workerId;
 35     // 數據標識id部分
 36     private final long datacenterId;
 37 
 38     public IdWorker(){
 39         this.datacenterId = getDatacenterId(maxDatacenterId);
 40         this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);
 41     }
 42     /**
 43      * @param workerId
 44      *            工作機器ID
 45      * @param datacenterId
 46      *            序列號
 47      */
 48     public IdWorker(long workerId, long datacenterId) {
 49         if (workerId > maxWorkerId || workerId < 0) {
 50             throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
 51         }
 52         if (datacenterId > maxDatacenterId || datacenterId < 0) {
 53             throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
 54         }
 55         this.workerId = workerId;
 56         this.datacenterId = datacenterId;
 57     }
 58     /**
 59      * 獲取下一個ID
 60      *
 61      * @return
 62      */
 63     public synchronized long nextId() {
 64         long timestamp = timeGen();
 65         if (timestamp < lastTimestamp) {
 66             throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
 67         }
 68 
 69         if (lastTimestamp == timestamp) {
 70             // 當前毫秒內,則+1
 71             sequence = (sequence + 1) & sequenceMask;
 72             if (sequence == 0) {
 73                 // 當前毫秒內計數滿了,則等待下一秒
 74                 timestamp = tilNextMillis(lastTimestamp);
 75             }
 76         } else {
 77             sequence = 0L;
 78         }
 79         lastTimestamp = timestamp;
 80         // ID偏移組合生成最終的ID,並返回ID
 81         long nextId = ((timestamp - twepoch) << timestampLeftShift)
 82                 | (datacenterId << datacenterIdShift)
 83                 | (workerId << workerIdShift) | sequence;
 84 
 85         return nextId;
 86     }
 87 
 88     private long tilNextMillis(final long lastTimestamp) {
 89         long timestamp = this.timeGen();
 90         while (timestamp <= lastTimestamp) {
 91             timestamp = this.timeGen();
 92         }
 93         return timestamp;
 94     }
 95 
 96     private long timeGen() {
 97         return System.currentTimeMillis();
 98     }
 99 
100     /**
101      * <p>
102      * 獲取 maxWorkerId
103      * </p>
104      */
105     protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) {
106         StringBuffer mpid = new StringBuffer();
107         mpid.append(datacenterId);
108         String name = ManagementFactory.getRuntimeMXBean().getName();
109         if (!name.isEmpty()) {
110             /*
111              * GET jvmPid
112              */
113             mpid.append(name.split("@")[0]);
114         }
115         /*
116          * MAC + PID 的 hashcode 獲取16個低位
117          */
118         return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
119     }
120 
121     /**
122      * <p>
123      * 數據標識id部分
124      * </p>
125      */
126     protected static long getDatacenterId(long maxDatacenterId) {
127         long id = 0L;
128         try {
129             InetAddress ip = InetAddress.getLocalHost();
130             NetworkInterface network = NetworkInterface.getByInetAddress(ip);
131             if (network == null) {
132                 id = 1L;
133             } else {
134                 byte[] mac = network.getHardwareAddress();
135                 id = ((0x000000FF & (long) mac[mac.length - 1])
136                         | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;
137                 id = id % (maxDatacenterId + 1);
138             }
139         } catch (Exception e) {
140             System.out.println(" getDatacenterId: " + e.getMessage());
141         }
142         return id;
143     }
144 }

 

3--測試

package com.anson;

/**
 * @description: TODO
 * @author: anson
 * @Date: 2019/10/7 22:16
 * @version: 1.0
 */
public class snow
{
    public static  void main(String[] args) throws Exception
    {
        try
        {


        IdWorker idw = new IdWorker(1,1);
        long ids = idw.nextId();


        for(int i=0;i<10000;i++)
        {
            ids = idw.nextId();
            System.out.println(ids);
        }

        }
        catch (Exception ex)
        {

        }

    }
}

結果如下圖:

 

 程式生成了19位的有序數字,這個既解決了分散式ID生成唯一性問題,也解決了性能問題,建議系統ID設計都採用該演算法生成。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ## 今日內容: 1. JQuery 高級 1. 動畫 2. 遍歷 3. 事件綁定 4. 案例 5. 插件 ## JQuery 高級 1. 動畫 1. 三種方式顯示和隱藏元素 1. 預設顯示和隱藏方式 1. show([speed,[easing],[fn]]) 1. 參數: 1. speed:動畫 ...
  • 前言 在開發的時候,有時在命令工具裡面,要多開兩個視窗分別啟動前端項目和後端服務介面,有沒有辦法將整個項目一起啟動呢 答案是有,前端和後端連載一起啟動,適用於前端為vue或React,後端為nodejs的項目。 只需用到一個npm包concurrently模塊,通過package.json配置實現。 ...
  • 示例代碼托管在: "http://www.github.com/dashnowords/blogs" 博客園地址: "《大史住在大前端》原創博文目錄" 華為雲社區地址: "【你要的前端打怪升級指南】" [TOC] B站地址: "【編譯原理】" Stanford公開課: "【Stanford大學公開課 ...
  • 一.window相關 二.body相關 三.滾動相關 四.位置精確定位 五.坐標軸 六.width/clientWidth/offsetWidth以及height之間區別 ...
  • ## 今日內容 1. JQuery 基礎: 1. 概念 2. 快速入門 3. JQuery對象和JS對象區別與轉換 4. 選擇器 5. DOM操作 6. 案例 # JQuery 基礎: 1. 概念: 一個JavaScript框架。簡化JS開發 * jQuery是一個快速、簡潔的JavaScript框 ...
  • 1、複雜動畫 (1)涉及到的屬性: animation-name:動畫名稱; animation-duration:單次動畫總時長; animation-timing-function:時間函數; animation-delay:播放前延時的時長; animation-iteration-count ...
  • 1. 類型強制轉換 1.1 string強制轉換為數字 可以用 *1來轉化為數字(實際上是調用 .valueOf方法) 然後使用 Number.isNaN來判斷是否為 NaN,或者使用 a!==a 來判斷是否為 NaN,因為 NaN!==NaN 常用:也可以使用 +來轉化字元串為數字 1.2 obj ...
  • 下表列出了用於操作HTML和CSS的所有方法。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...