一、雪花算法介紹
雪花算法是Twitter公司開源的一種用於生成全局唯一ID的算法,它可以保證在分佈式的情況下生成唯一ID,解決了傳統的遞增ID在分佈式場景下可能重複的問題。
雪花算法生成的ID是一個64位的長整型,由以下幾部分構成:
- 時間戳
- 機器ID
- 序列號
其中,時間戳佔用41位,可以表示的時間範圍為2^41/(1000*60*60*24*365),大約為69年。機器ID佔用10位,可以表示的最大機器數為2^10-1,即1023個。序列號佔用12位,可以表示的最大序號數為2^12-1,即4095個。
二、Java實現雪花算法
1、時間戳
首先,我們需要獲取當前時間的時間戳。Java中可以使用System.currentTimeMillis()方法獲取當前時間戳,不過它只能精確到毫秒級別,為了讓我們的ID更加唯一,我們可以使用System.nanoTime()方法獲取更加精確的時間戳,精確到納秒級別。
/**
* 獲取當前時間戳,精確到納秒級別
* @return 當前時間戳(毫秒+納秒)
*/
private static long getCurrentTimestamp() {
long timestamp = System.nanoTime();
return (timestamp - START_TIMESTAMP) / 1000000;
}
其中,START_TIMESTAMP是一個固定時間戳,用於計算時間戳的相對值。由於時間戳只佔用了41位,所以時間戳的範圍是有限的,為了避免時間戳滿足之後,衝突概率變高的問題,我們可以將START_TIMESTAMP設置為一個固定的值,比如2018年1月1日。
2、機器ID
雪花算法中,機器ID是用於指示不同機器的一個標識符。在分佈式系統中,不同的機器需要有不同的機器ID。可以通過配置文件等方式來獲取機器ID,也可以使用網卡MAC地址等實際唯一的標識符來獲取。
/**
* 獲取機器ID,可以根據實際情況進行獲取
* @return 機器ID
*/
private static long getMachineId() {
// TODO 根據實際情況獲取機器ID
return 1;
}
3、序列號
序列號用於保證同一時間內,同一台機器生成不同的ID。為了避免序列號重複,我們需要在同一毫秒內,使用不同的序列號。另外,由於序列號只佔用了12位,所以最多可以有4095個序列號,為了避免序列號使用完之後,需要等待到下一毫秒,我們可以將序列號的初始值設置為一個隨機數。
/**
* 獲取序列號,同一毫秒內生成不同的序列號
* @return 序列號
*/
private static long getSequence() {
long sequence = sequenceGenerator.getAndIncrement();
return sequence % SEQUENCE_LIMIT;
}
其中,sequenceGenerator是一個AtomicLong類型的變量,用於產生序列號,這個變量在程序啟動時,會被初始化為一個隨機數,用於保證每次重啟程序時,序列號不會從0開始計數。
三、Java實現雪花算法示例代碼
下面是一個完整的Java實現雪花算法的示例代碼:
/**
* 雪花算法生成全局唯一ID
*/
public class SnowflakeIdGenerator {
// 起始的時間戳:2018-01-01
private static final long START_TIMESTAMP = 1514736000000L;
// 機器ID所佔的位數
private static final long MACHINE_ID_BITS = 10L;
// 序列號所佔的位數
private static final long SEQUENCE_BITS = 12L;
// 支持的最大機器ID,結果是1023
private static final long MAX_MACHINE_ID = ~(-1L << MACHINE_ID_BITS);
// 支持的最大序列號,結果是4095
private static final long SEQUENCE_LIMIT = ~(-1L < MAX_MACHINE_ID || machineId < 0) {
throw new IllegalArgumentException("MachineId can't be greater than " + MAX_MACHINE_ID + " or less than 0.");
}
}
/**
* 生成唯一ID
* @return 返回64位的唯一ID
*/
public static synchronized long generateId() {
long timestamp = getCurrentTimestamp();
if(timestamp < 0) {
throw new IllegalStateException("Clock moved backwards, refuse to generate id.");
}
long sequence = getSequence();
long id = ((timestamp - START_TIMESTAMP) << TIMESTAMP_SHIFT) |
(machineId << MACHINE_ID_SHIFT) |
sequence;
return id;
}
/**
* 獲取當前時間戳,精確到納秒級別
* @return 當前時間戳(毫秒+納秒)
*/
private static long getCurrentTimestamp() {
long timestamp = System.nanoTime();
return (timestamp - START_TIMESTAMP) / 1000000;
}
/**
* 獲取機器ID,可以根據實際情況進行獲取
* @return 機器ID
*/
private static long getMachineId() {
// TODO 根據實際情況獲取機器ID
return 1;
}
/**
* 獲取序列號,同一毫秒內生成不同的序列號
* @return 序列號
*/
private static long getSequence() {
long sequence = sequenceGenerator.getAndIncrement();
return sequence % SEQUENCE_LIMIT;
}
}
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hk/n/242418.html