欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Mysql中雪花算法(Snowflake)的使用

 更新時間:2025年02月25日 08:30:11   作者:常生果  
雪花算法是一種生成全局唯一ID的分布式算法,本文就來介紹一下Mysql中雪花算法的使用,具有一定的參考價值,感興趣的可以了解一下

一、基本概念

雪花算法(Snowflake)是一種生成全局唯一ID的分布式算法。它的主要功能是在分布式系統(tǒng)中生成一個全局唯一的ID,且ID是按照時間有序遞增的。

Snowflake 中文的意思為雪花,所以 Snowflake算法 常被稱為 雪花算法,是 Twitter(現(xiàn)“X”)開源的分布式 ID 生成算法,是一種分布式主鍵ID生成的解決方案。雪花算法生成后是一個 64bit 的 long 型的數(shù)值,組成部分引入了時間戳,基本保持了自增。

互聯(lián)網(wǎng)大廠實現(xiàn)的雪花開源項目:

美團 Leaf:https://github.com/Meituan-Dianping/Leaf

百度 Uid:https://github.com/baidu/uid-generator

二、核心思想

Snowflake算法使用一個64位的二進(jìn)制數(shù)字作為ID。這64位long型ID被分割成四個部分:符號位、時間戳、工作機器ID、序列號。通過這幾部分來表示不同的信息,將數(shù)據(jù)映射到具有特定結(jié)構(gòu)的分布式系統(tǒng)中,實現(xiàn)數(shù)據(jù)的存儲和查詢。

該算法由一系列節(jié)點組成,每個節(jié)點負(fù)責(zé)存儲數(shù)據(jù)的一部分。這些節(jié)點通過哈希函數(shù)將數(shù)據(jù)映射到特定的位置,形成類似于雪花結(jié)構(gòu)的分布式系統(tǒng)。通過這種方式,雪花算法能夠在分布式系統(tǒng)中保證ID的唯一性和有序性。

因為雪花算法有序自增,保障了 MySQL 中 B+ Tree 索引結(jié)構(gòu)插入高性能。所以,日常業(yè)務(wù)使用中,雪花算法更多是被應(yīng)用在數(shù)據(jù)庫的主鍵 ID 和業(yè)務(wù)關(guān)聯(lián)主鍵。

上面有說過雪花算法會生成 64bit 的 long 型的數(shù)值,而這64bit 可以分為四個組成部分:

固定值:

1bit,最高位是符號位,0 表示正,1 表示負(fù),固定為 0,如果是 1 就是負(fù)數(shù)了。

第一位為什么不使用

在雪花算法中,第一位是符號位,0表示整數(shù),第一位如果是1則表示負(fù)數(shù),我們用的ID默認(rèn)就是正數(shù),所以默認(rèn)就是0,那么這一位默認(rèn)就沒有意義。

時間戳:

41bit,存儲毫秒級時間戳(41 位的長度可以使用 69 年)。

標(biāo)識位(存儲機器碼):

10bit,上面中的 機器id(5bit)和 服務(wù)id(5bit)統(tǒng)一叫作“標(biāo)識位”,兩個標(biāo)識位組合起來最多可以支持部署 1024 個節(jié)點。

標(biāo)識位怎么用

標(biāo)識位一共10 bit,如果全部表示機器,那么可以表示1024臺機器,如果拆分,5 bit 表示機房,5bit表示機房里面的機器,那么可以有32個機房,每個機房可以用32臺機器。

序列號:

12bit,用于表示在同一毫秒內(nèi)生成的多個ID的序號。如果在同一毫秒內(nèi)生成的ID超過了4096個(2的12次方),則需要等到下一毫秒再生成ID。

默認(rèn)的雪花算法是 64 bit,具體的長度可以自行配置。

如果希望運行更久,增加時間戳的位數(shù);如果需要支持更多節(jié)點部署,增加標(biāo)識位長度;如果并發(fā)很高,增加序列號位數(shù)。

總結(jié)雪花算法并不是一成不變的,可以根據(jù)系統(tǒng)內(nèi)具體場景進(jìn)行定制。

三、為何要使用雪花算法

?現(xiàn)在的服務(wù)基本是分布式、微服務(wù)形式的,而且大數(shù)據(jù)量也導(dǎo)致分庫分表的產(chǎn)生,對于水平分表就需要保證表中 id 的全局唯一性。 對于 MySQL 而言,一個表中的主鍵 id 一般使用自增的方式,但是如果進(jìn)行水平分表之后,多個表中會生成重復(fù)的 id 值。那么如何保證水平分表后的多張表中的 id 是全局唯一性的呢?。

1,數(shù)據(jù)庫主鍵自增:
可以讓不同表初始化一個不同的初始值,然后按指定的步長進(jìn)行自增。
例如有3張拆分表,初始主鍵值為1,2,3,自增步長為3。

2,UUID:
用 UUID 來作為主鍵,但是 UUID 生成的是一個無序的字符串,
對于 MySQL 推薦使用增長的數(shù)值類型值作為主鍵來說不適合。

3,Redis:
使用 Redis 的自增原子性來生成唯一 id,但是這種方式業(yè)內(nèi)比較少用。

當(dāng)然還有其他解決方案,不同互聯(lián)網(wǎng)公司也有自己內(nèi)部的實現(xiàn)方案。雪花算法廣泛應(yīng)用于分布式系統(tǒng)中的唯一ID生成。它可以保證在分布式環(huán)境中生成的ID是唯一且有序的。常見的應(yīng)用場合包括訂單號生成、分布式數(shù)據(jù)庫中的數(shù)據(jù)主鍵、分布式鎖等。通過使用雪花算法生成全局唯一ID,可以方便地進(jìn)行分布式系統(tǒng)的數(shù)據(jù)管理和查詢。

優(yōu)點:

缺點:

 依賴服務(wù)器時間,服務(wù)器時間回?fù)軙r可能會生成重復(fù) id。

小小的解決方案:算法中可通過記錄最后一個生成 id 時的時間戳來解決,每次生成 id 之前比較當(dāng)前服務(wù)器時鐘是否被回?fù)?,避免生成重?fù) id。

由于時間回?fù)軐?dǎo)致的生產(chǎn)重復(fù)的ID的問題,其實百度和美團都有自己的解決方案了,有興趣可以去看看。

美團 Leaf:https://github.com/Meituan-Dianping/Leaf

百度 Uid:https://github.com/baidu/uid-generator

四、代碼實現(xiàn)

以下是雪花算法的Java代碼實現(xiàn)示例:

public class SnowflakeIdWorker{
    /** 開始時間截 (2015-01-01) */
    private final long twepoch = 1288834974657L;
 
    /** 機器id所占的位數(shù) */
    private final long workerIdBits = 5L;
 
    /** 數(shù)據(jù)標(biāo)識id所占的位數(shù) */
    private final long datacenterIdBits = 5L;
 
    /** 支持的最大機器id,結(jié)果是31 (這個移位算法可以很快的計算出幾位二進(jìn)制數(shù)所能表示的最大十進(jìn)制數(shù)) */
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
 
    /** 支持的最大數(shù)據(jù)標(biāo)識id,結(jié)果是31 */
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
 
    /** 序列在id中占的位數(shù) */
    private final long sequenceBits = 12L;
 
    /** 機器ID向左移12位 */
    private final long workerIdShift = sequenceBits;
 
    /** 數(shù)據(jù)標(biāo)識id向左移17位(12+5) */
    private final long datacenterIdShift = sequenceBits + workerIdBits;
 
    /** 時間截向左移22位(5+5+12) */
    private final long timestampLeftShift = sequenceBits + workerIdBits
            + datacenterIdBits;
 
    /** 生成序列的掩碼,這里為4095 (0b111111111111=0xfff=4095) */
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);
 
    /** 工作機器ID(0~31) */
    private long workerId;
 
    /** 數(shù)據(jù)中心ID(0~31) */
    private long datacenterId;
 
    /** 毫秒內(nèi)序列(0~4095) */
    private long sequence = 0L;
 
    /** 上次生成ID的時間截 */
    private long lastTimestamp = -1L;
 
    /**
     * 構(gòu)造函數(shù)
     *
     * @param workerId
     *            工作ID (0~31)
     * @param datacenterId
     *            數(shù)據(jù)中心ID (0~31)
     */
    public SnowflakeIdWorker(long workerId, long datacenterId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format(
                    "worker Id can't be greater than %d or less than 0",
                    maxWorkerId));
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException(String.format(
                    "datacenter Id can't be greater than %d or less than 0",
                    maxDatacenterId));
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }
 
    /**
     * 獲得下一個ID (該方法是線程安全的)
     *
     * @return SnowflakeId
     */
    public synchronized long nextId() {
        long timestamp = timeGen();
 
        // 如果當(dāng)前時間小于上一次ID生成的時間戳,說明系統(tǒng)時鐘回退過這個時候應(yīng)當(dāng)拋出異常
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(
                    String.format(
                            "Clock moved backwards.  Refusing to generate id for %d milliseconds",
                            (lastTimestamp - timestamp)));
        }
 
        // 如果是同一時間生成的,則進(jìn)行毫秒內(nèi)序列
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            // 毫秒內(nèi)序列溢出
            if (sequence == 0) {
                // 阻塞到下一個毫秒,獲得新的時間戳
                timestamp = tilNextMillis(lastTimestamp);
            }
        }
        // 時間戳改變,毫秒內(nèi)序列重置
        else {
            sequence = 0L;
        }
 
        // 上次生成ID的時間截
        lastTimestamp = timestamp;
 
        // 移位并通過或運算拼到一起組成64位的ID
        return ((timestamp - twepoch) << timestampLeftShift) //
                | (datacenterId << datacenterIdShift) //
                | (workerId << workerIdShift) //
                | sequence;
    }
 
    /**
     * 阻塞到下一個毫秒,直到獲得新的時間戳
     *
     * @param lastTimestamp
     *            上次生成ID的時間截
     * @return 當(dāng)前時間戳
     */
    protected long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }
 
    /**
     * 返回以毫秒為單位的當(dāng)前時間
     *
     * @return 當(dāng)前時間(毫秒)
     */
    protected long timeGen() {
        return System.currentTimeMillis();
    }
 
    //測試方法
    public static void main(String[] args) {
 
        // 假設(shè)我們有一個工作機器ID為1,數(shù)據(jù)中心ID為1的環(huán)境
        long workerId = 1L;
        long datacenterId = 1L;
        
        // 創(chuàng)建一個SnowflakeIdWorker實例
        SnowflakeIdWorker idWorker = new SnowflakeIdWorker(workerId, datacenterId);
        
        // 生成并打印10個ID作為示例
        for (int i = 0; i < 10; i++) {
            long id = idWorker.nextId();
            System.out.println(id);
        }
    }
 
}

五、總結(jié)

雪花算法依賴于時間的一致性,如果發(fā)生時間回?fù)?,可能會?dǎo)致問題。為了解決這個問題,通常會使用拓展位來擴展時間戳的位數(shù)。原本雪花算法只能支持69年的時間范圍,但根據(jù)實際需求,可以增加時間戳的位數(shù)來延長可使用的年限,比如使用42位可以支持139年的時間范圍。然而,對于很多公司來說,首要任務(wù)是生存下來,因此可能會權(quán)衡取舍,不過度追求時間戳位數(shù)的增加。

需要注意的是,雪花算法也有一些缺點。在單機上,生成的ID是遞增的,但在多臺機器上,只能大致保持遞增趨勢,并不能嚴(yán)格保證遞增。這是因為多臺機器之間的時鐘不一定完全同步。因此,在多機器環(huán)境下,對于嚴(yán)格的遞增需求,需要考慮其他解決方案。

總而言之,雪花算法是一種常用的分布式唯一ID生成算法,但并非完美解決方案。在使用時,需要根據(jù)實際需求和限制條件進(jìn)行權(quán)衡和選擇,以尋找適合自己情況的解決方案。

到此這篇關(guān)于Mysql中雪花算法(Snowflake)的使用的文章就介紹到這了,更多相關(guān)Mysql 雪花算法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • mysql鎖定單個表的方法

    mysql鎖定單個表的方法

    這篇文章主要介紹了mysql鎖定單個表的方法 ,需要的朋友可以參考下
    2014-05-05
  • 詳解MySQL單列索引和聯(lián)合索引

    詳解MySQL單列索引和聯(lián)合索引

    聯(lián)合索引的結(jié)構(gòu)與電話簿類似,人名由姓和名構(gòu)成,電話簿首先按姓氏進(jìn)行排序,然后按名字對有相同姓氏的人進(jìn)行排序,這篇文章主要介紹了MySQL單列索引和聯(lián)合索引,需要的朋友可以參考下
    2022-09-09
  • MySql中的連接查詢問題

    MySql中的連接查詢問題

    這篇文章主要介紹了MySql中的連接查詢問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • Linux自動備份MySQL數(shù)據(jù)庫腳本代碼

    Linux自動備份MySQL數(shù)據(jù)庫腳本代碼

    下面這段Linux的Shell腳本用于每日自動備份MySQL數(shù)據(jù)庫,可通過Linux的crontab每天定時執(zhí)行
    2013-11-11
  • MySQL中慢SQL優(yōu)化的不同方式介紹

    MySQL中慢SQL優(yōu)化的不同方式介紹

    慢 SQL 的優(yōu)化,主要從兩個方面考慮,SQL 語句本身的優(yōu)化,以及數(shù)據(jù)庫設(shè)計的優(yōu)化,下面小編就來給大家介紹一下有哪些方式可以優(yōu)化慢 SQL吧
    2025-03-03
  • 通過SqlCmd執(zhí)行超大SQL文件的方法

    通過SqlCmd執(zhí)行超大SQL文件的方法

    這篇文章主要介紹了sql?server?與?mysql?中常用的SQL語句區(qū)別,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-12-12
  • IPv6設(shè)置后如何解決MySQL無法連接localhost的問題

    IPv6設(shè)置后如何解決MySQL無法連接localhost的問題

    這篇文章主要介紹了IPv6設(shè)置后如何解決MySQL無法連接localhost的問題,需要的朋友可以參考下
    2016-04-04
  • Unity連接MySQL并讀取表格數(shù)據(jù)的實現(xiàn)代碼

    Unity連接MySQL并讀取表格數(shù)據(jù)的實現(xiàn)代碼

    本文給大家介紹Unity連接MySQL并讀取表格數(shù)據(jù)的實現(xiàn)代碼,實例化的同時調(diào)用MySqlConnection,傳入?yún)?shù),這里的傳入?yún)?shù)個人認(rèn)為是CMD里面的直接輸入了,string格式直接類似手敲到cmd里面,完整代碼參考下本文
    2021-06-06
  • Mysql空間清理的幾種具體方法

    Mysql空間清理的幾種具體方法

    在Mysql環(huán)境下,常常由于數(shù)據(jù)磁盤滿而導(dǎo)致Mysql故障。本文就介紹了幾種清理方法,具有一定的參考價值,感興趣的可以了解一下
    2021-09-09
  • 兩個windows服務(wù)器使用canal實現(xiàn)mysql實時同步

    兩個windows服務(wù)器使用canal實現(xiàn)mysql實時同步

    canal是阿里基于java寫的一個組件,他的作用是canal.deployer讀取mysql數(shù)據(jù)的binlog日志,然后canal.adapter將其轉(zhuǎn)換為對應(yīng)的數(shù)據(jù)(數(shù)據(jù)的變化或者變化后的數(shù)據(jù),跟配置有關(guān)),并且同步到相關(guān)中間件,本文實現(xiàn)兩個windows服務(wù)器使用canal實現(xiàn)mysql主從復(fù)制實時同步
    2025-03-03

最新評論