日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

long 雪花算法_雪花算法

發布時間:2025/4/16 编程问答 49 豆豆
生活随笔 收集整理的這篇文章主要介紹了 long 雪花算法_雪花算法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

小背景:我們的訂單編號要求是 16 位,改造了一下雪花算法

*

* 參考Twitter Snowflake算法,按實際需求,做了部分修改,結構如下(每部分用-分開):

* 0000000000 - 10000000000000000000000000000000000000000 - 00 - 000 - 00000000?

* 10位不使用,因為目的是為了最終生成16位整數,所以只使用后面的54bit

* 41位時間截(毫秒級),存儲時間截的差值(當前時間截 - 開始時間截),41位的時間截,可以使用69年,且考慮到差值較小時,會生成不足16位的數字,因些需要選擇一個合適的值

* 2位的集群ID,可以部署在4個集群

* 3位的節點ID,每個集群可以有8個節點

* 8位序列,毫秒內的計數,支持每個節點每毫秒產生256個ID序號

* 加起來剛好64位,為一個Long型

*/public class UniqueIdWorker {

/**

* 起始時間,用于調整位數

* 這里取值 2012-12-22 00:00:00

* 以41位表示毫秒,此方案可以使用到 2082-08-28 15:47:35,訂單編號從15開頭,

*/ private final long baseTimestamp = 1356105600000L;

/**

* 機器id所占的位數

*/

private final long workerIdBits = 3L;

/**

* 集群id所占的位數

*/

private final long clusterIdBits = 2L;

/**

* 支持的最大機器id

*/ private final long maxWorkerId = -1L ^ (-1L << workerIdBits);

/**

* 支持的最大集群id

*/ private final long maxClusterId = -1L ^ (-1L << clusterIdBits);

/**

* 序列在id中占的位數

*/

private final long sequenceBits = 8L;

/**

* 機器ID向左移位數

*/

private final long workerIdShift = sequenceBits;

/**

* 集群id向左移位數

*/

private final long clusterIdShift = sequenceBits + workerIdBits;

/**

* 時間截向左移位數

*/

private final long timestampLeftShift = sequenceBits + workerIdBits + clusterIdBits;

/**

* 生成序列的掩碼

*/

private final long sequenceMask = -1L ^ (-1L << sequenceBits);

/**

* 工作機器ID

*/ private long workerId;

/**

* 集群ID

*/ private long clusterId;

/**

* 毫秒內序列

*/

private long sequence = 0L;

/**

* 上次生成ID的時間截

*/

private long lastTimestamp = -1L;

/**

* 構造函數

*

* @param workerId

* @param clusterId

*/

public UniqueIdWorker(Long workerId, Long clusterId) {

Preconditions.checkArgument(null != workerId && workerId > 0 && workerId < maxWorkerId, "Invalid workerId");

Preconditions.checkArgument(null != clusterId && clusterId > 0 && clusterId < maxClusterId, "Invalid clusterId");

this.workerId = workerId;

this.clusterId = clusterId;

}

/**

* 獲得下一個ID

* * @return

*/

public synchronized long nextId() {

long timestamp = timeGen();

//系統時鐘回退,拋出異常

if (timestamp < lastTimestamp) {

throw new RuntimeException(String.format("Clock moved backwards. Failed to generate id for %d milliseconds", lastTimestamp - timestamp));

}

//同一毫秒內順序遞增

if (lastTimestamp == timestamp) {

sequence = (sequence + 1) & sequenceMask;

//毫秒內序列溢出

if (sequence == 0) {

//阻塞到下一個毫秒,獲得新的時間戳

timestamp = tilNextMillis(lastTimestamp);

}

}

//時間戳改變重置為0

else {

sequence = 0L;

}

lastTimestamp = timestamp;

return ((timestamp - baseTimestamp) << timestampLeftShift)

| (clusterId << clusterIdShift)

| (workerId << workerIdShift)

| sequence;

}

/**

* 阻塞到下一個毫秒的時間戳并返回

*

* @param lastTimestamp

* @return

*/

private long tilNextMillis(long lastTimestamp) {

long timestamp = timeGen();

while (timestamp <= lastTimestamp) {

timestamp = timeGen();

}

return timestamp;

}

/**

* 返回當前毫秒時間戳

*

* @return

*/

private long timeGen() {

return System.currentTimeMillis();

}

/**

* 根據訂單ID反向解析內容

*

* @param id

* @return

*/

public String parseId(Long id) {

if (null == id) {

return "";

}

return String.format("sequence: %d, workerId: %d, clusterId: %d, timestamp: %d\n", ((id) & ~(-1L << sequenceBits))

, ((id >> (workerIdShift)) & ~(-1L << (workerIdBits)))

, ((id >> clusterIdShift) & ~(-1L << clusterIdBits))

, ((id >> timestampLeftShift) + baseTimestamp));

}

}

解釋

41 位時間戳能用幾年?

@Test

public void test2() {

String minTimeStampStr = "00000000000000000000000000000000000000000";

long minTimeStamp = new BigInteger(minTimeStampStr, 2).longValue();

String maxTimeStampStr = "11111111111111111111111111111111111111111";

long maxTimeStamp = new BigInteger(maxTimeStampStr, 2).longValue();

long oneYearMills = 1L * 1000 * 60 * 60 * 24 * 365;

System.out.println((maxTimeStamp - minTimeStamp) / oneYearMills);

}

結果是 69

前 41 位最小值

如果前 41 位太小,結果可能不滿 16 位。

計算 1000_0000_0000_0000L 的前 41 位

@Test

public void test1() {

String str = Long.toBinaryString(

1000_0000_0000_0000L);//00001110001101011111101010010011000110100_0000000000000

// String str = Long.toBinaryString(9999_9999_9999_9999L);//10001110000110111100100110111111000000111_1111111111111

System.out.println(str);

int needZero = 54 - str.length();

str = StringUtils.repeat("0", needZero) + str;

char[] chars = str.toCharArray();

System.out.println("length :" + chars.length);

for (int i = 0; i < chars.length; i++) {

if (i != 0 && i % 41 == 0) {

System.out.print("_");

}

System.out.print(chars[i]);

}

}

起始時間計算

用當前時間戳減去前 41 位最小值,得到的時間就是起始時間,如果需要開頭從 15 或者 20 開始,也可以自行計算。

@Test

public void test3() {

long curTimeStamp = System.currentTimeMillis();

//也可以用下面的

// long curTimeStamp1 = LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant()

// .toEpochMilli();

System.out.println(curTimeStamp);

// System.out.println(curTimeStamp1);

//差值最小為00001110001101011111101010010011000110100

String diffTimeStampStr = "00001110001101011111101010010011000110100";

long diffTimeStamp = new BigInteger(diffTimeStampStr, 2).longValue();

long minTimeStamp = curTimeStamp - diffTimeStamp;

LocalDateTime minDateTime = LocalDateTime

.ofInstant(Instant.ofEpochMilli(minTimeStamp), ZoneId.systemDefault());

System.out.println(minDateTime);//2015-02-15T17:59:14.079

}

-1L ^ (-1L << workerIdBits)求最大機器 id

//-1 的二進制原碼1000 0001,反碼 - 1111 1111

//-1 << 3也就是-8的二進制 1000 1000 反碼- 1111 1000

// 1111 1111 ^ 1111 1000 = 0000 0111

總結

以上是生活随笔為你收集整理的long 雪花算法_雪花算法的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。