long 雪花算法_雪花算法
小背景:我們的訂單編號(hào)要求是 16 位,改造了一下雪花算法
*
* 參考Twitter Snowflake算法,按實(shí)際需求,做了部分修改,結(jié)構(gòu)如下(每部分用-分開(kāi)):
* 0000000000 - 10000000000000000000000000000000000000000 - 00 - 000 - 00000000?
* 10位不使用,因?yàn)槟康氖菫榱俗罱K生成16位整數(shù),所以只使用后面的54bit
* 41位時(shí)間截(毫秒級(jí)),存儲(chǔ)時(shí)間截的差值(當(dāng)前時(shí)間截 - 開(kāi)始時(shí)間截),41位的時(shí)間截,可以使用69年,且考慮到差值較小時(shí),會(huì)生成不足16位的數(shù)字,因些需要選擇一個(gè)合適的值
* 2位的集群ID,可以部署在4個(gè)集群
* 3位的節(jié)點(diǎn)ID,每個(gè)集群可以有8個(gè)節(jié)點(diǎn)
* 8位序列,毫秒內(nèi)的計(jì)數(shù),支持每個(gè)節(jié)點(diǎn)每毫秒產(chǎn)生256個(gè)ID序號(hào)
* 加起來(lái)剛好64位,為一個(gè)Long型
*/public class UniqueIdWorker {
/**
* 起始時(shí)間,用于調(diào)整位數(shù)
* 這里取值 2012-12-22 00:00:00
* 以41位表示毫秒,此方案可以使用到 2082-08-28 15:47:35,訂單編號(hào)從15開(kāi)頭,
*/ private final long baseTimestamp = 1356105600000L;
/**
* 機(jī)器id所占的位數(shù)
*/
private final long workerIdBits = 3L;
/**
* 集群id所占的位數(shù)
*/
private final long clusterIdBits = 2L;
/**
* 支持的最大機(jī)器id
*/ private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
/**
* 支持的最大集群id
*/ private final long maxClusterId = -1L ^ (-1L << clusterIdBits);
/**
* 序列在id中占的位數(shù)
*/
private final long sequenceBits = 8L;
/**
* 機(jī)器ID向左移位數(shù)
*/
private final long workerIdShift = sequenceBits;
/**
* 集群id向左移位數(shù)
*/
private final long clusterIdShift = sequenceBits + workerIdBits;
/**
* 時(shí)間截向左移位數(shù)
*/
private final long timestampLeftShift = sequenceBits + workerIdBits + clusterIdBits;
/**
* 生成序列的掩碼
*/
private final long sequenceMask = -1L ^ (-1L << sequenceBits);
/**
* 工作機(jī)器ID
*/ private long workerId;
/**
* 集群ID
*/ private long clusterId;
/**
* 毫秒內(nèi)序列
*/
private long sequence = 0L;
/**
* 上次生成ID的時(shí)間截
*/
private long lastTimestamp = -1L;
/**
* 構(gòu)造函數(shù)
*
* @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;
}
/**
* 獲得下一個(gè)ID
* * @return
*/
public synchronized long nextId() {
long timestamp = timeGen();
//系統(tǒng)時(shí)鐘回退,拋出異常
if (timestamp < lastTimestamp) {
throw new RuntimeException(String.format("Clock moved backwards. Failed to generate id for %d milliseconds", lastTimestamp - timestamp));
}
//同一毫秒內(nèi)順序遞增
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & sequenceMask;
//毫秒內(nèi)序列溢出
if (sequence == 0) {
//阻塞到下一個(gè)毫秒,獲得新的時(shí)間戳
timestamp = tilNextMillis(lastTimestamp);
}
}
//時(shí)間戳改變重置為0
else {
sequence = 0L;
}
lastTimestamp = timestamp;
return ((timestamp - baseTimestamp) << timestampLeftShift)
| (clusterId << clusterIdShift)
| (workerId << workerIdShift)
| sequence;
}
/**
* 阻塞到下一個(gè)毫秒的時(shí)間戳并返回
*
* @param lastTimestamp
* @return
*/
private long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
/**
* 返回當(dāng)前毫秒時(shí)間戳
*
* @return
*/
private long timeGen() {
return System.currentTimeMillis();
}
/**
* 根據(jù)訂單ID反向解析內(nèi)容
*
* @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 位時(shí)間戳能用幾年?
@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);
}
結(jié)果是 69
前 41 位最小值
如果前 41 位太小,結(jié)果可能不滿 16 位。
計(jì)算 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]);
}
}
起始時(shí)間計(jì)算
用當(dāng)前時(shí)間戳減去前 41 位最小值,得到的時(shí)間就是起始時(shí)間,如果需要開(kāi)頭從 15 或者 20 開(kāi)始,也可以自行計(jì)算。
@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)求最大機(jī)器 id
//-1 的二進(jìn)制原碼1000 0001,反碼 - 1111 1111
//-1 << 3也就是-8的二進(jìn)制 1000 1000 反碼- 1111 1000
// 1111 1111 ^ 1111 1000 = 0000 0111
總結(jié)
以上是生活随笔為你收集整理的long 雪花算法_雪花算法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: js导出的xlsx无法打开_js-xls
- 下一篇: python设计拼图小游戏_教你用Pyt