Mysql中雪花算法(Snowflake)的使用
一、基本概念
雪花算法(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)文章
Linux自動備份MySQL數(shù)據(jù)庫腳本代碼
下面這段Linux的Shell腳本用于每日自動備份MySQL數(shù)據(jù)庫,可通過Linux的crontab每天定時執(zhí)行2013-11-11IPv6設(shè)置后如何解決MySQL無法連接localhost的問題
這篇文章主要介紹了IPv6設(shè)置后如何解決MySQL無法連接localhost的問題,需要的朋友可以參考下2016-04-04Unity連接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兩個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