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

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

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

一、基本概念

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

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

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

美團(tuán) Leaf:https://github.com/Meituan-Dianping/Leaf

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

二、核心思想

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

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

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

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

固定值:

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

第一位為什么不使用

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

時(shí)間戳:

41bit,存儲(chǔ)毫秒級(jí)時(shí)間戳(41 位的長(zhǎng)度可以使用 69 年)。

標(biāo)識(shí)位(存儲(chǔ)機(jī)器碼):

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

標(biāo)識(shí)位怎么用

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

序列號(hào):

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

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

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

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

三、為何要使用雪花算法

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

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

2,UUID:
用 UUID 來(lái)作為主鍵,但是 UUID 生成的是一個(gè)無(wú)序的字符串,
對(duì)于 MySQL 推薦使用增長(zhǎng)的數(shù)值類(lèi)型值作為主鍵來(lái)說(shuō)不適合。

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

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

優(yōu)點(diǎn):

缺點(diǎn):

 依賴(lài)服務(wù)器時(shí)間,服務(wù)器時(shí)間回?fù)軙r(shí)可能會(huì)生成重復(fù) id。

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

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

美團(tuán) Leaf:https://github.com/Meituan-Dianping/Leaf

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

四、代碼實(shí)現(xiàn)

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

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

五、總結(jié)

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

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

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

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

相關(guān)文章

  • mysql鎖定單個(gè)表的方法

    mysql鎖定單個(gè)表的方法

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

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

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

    MySql中的連接查詢(xún)問(wèn)題

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

最新評(píng)論