雪花算法的原始版本是scala版,用于生成分布式ID(纯数字,时间顺序),订单编号等。
自增ID:对于数据敏感场景不宜使用,且不适合于分布式场景。
GUID:采用无意义字符串,数据量增大时造成访问过慢,且不宜排序。
算法描述:
雪花算法是一款开源的ID算法。主要的组成:
最高位是符号位,始终为0,不可用。
41位的时间序列,精确到毫秒级,41位的长度可以使用69年。时间位还有一个很重要的作用是可以根据时间进行排序。
10位的机器标识,10位的长度最多支持部署1024个节点。
12位的计数序列号,序列号即一系列的自增id,可以支持同一节点同一毫秒生成多个ID序号,12位的计数序列号支持每个节点每毫秒产生4096个ID序号
package com.xiaoxiao.utils;
import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.NetworkInterface;
/**
* <p>鍚嶇О锛欼dWorker.java</p>
* <p>鎻忚堪锛氬垎甯冨紡鑷闀縄D</p>
* <pre>
* Twitter鐨� Snowflake銆�JAVA瀹炵幇鏂规
* </pre>
* 鏍稿績浠g爜涓哄叾IdWorker杩欎釜绫诲疄鐜帮紝鍏跺師鐞嗙粨鏋勫涓嬶紝鎴戝垎鍒敤涓�涓�0琛ㄧず涓�浣嶏紝鐢ㄢ�斿垎鍓插紑閮ㄥ垎鐨勪綔鐢細
* 1||0---0000000000 0000000000 0000000000 0000000000 0 --- 00000 ---00000 ---000000000000
* 鍦ㄤ笂闈㈢殑瀛楃涓蹭腑锛岀涓�浣嶄负鏈娇鐢紙瀹為檯涓婁篃鍙綔涓簂ong鐨勭鍙蜂綅锛夛紝鎺ヤ笅鏉ョ殑41浣嶄负姣绾ф椂闂达紝
* 鐒跺悗5浣峝atacenter鏍囪瘑浣嶏紝5浣嶆満鍣↖D锛堝苟涓嶇畻鏍囪瘑绗︼紝瀹為檯鏄负绾跨▼鏍囪瘑锛夛紝
* 鐒跺悗12浣嶈姣鍐呯殑褰撳墠姣鍐呯殑璁℃暟锛屽姞璧锋潵鍒氬ソ64浣嶏紝涓轰竴涓狶ong鍨嬨��
* 杩欐牱鐨勫ソ澶勬槸锛屾暣浣撲笂鎸夌収鏃堕棿鑷鎺掑簭锛屽苟涓旀暣涓垎甯冨紡绯荤粺鍐呬笉浼氫骇鐢烮D纰版挒锛堢敱datacenter鍜屾満鍣↖D浣滃尯鍒嗭級锛�
* 骞朵笖鏁堢巼杈冮珮锛岀粡娴嬭瘯锛宻nowflake姣忕鑳藉浜х敓26涓嘔D宸﹀彸锛屽畬鍏ㄦ弧瓒抽渶瑕併��
* <p>
* 64浣岻D (42(姣)+5(鏈哄櫒ID)+5(涓氬姟缂栫爜)+12(閲嶅绱姞))
*闆姳绠楁硶ID鐢熸垚鍣�
* @author Polim
*/
public class IdWorker
{
// 鏃堕棿璧峰鏍囪鐐癸紝浣滀负鍩哄噯锛屼竴鑸彇绯荤粺鐨勬渶杩戞椂闂达紙涓�鏃︾‘瀹氫笉鑳藉彉鍔級
private final static long twepoch = 1288834974657L;
// 鏈哄櫒鏍囪瘑浣嶆暟
private final static long workerIdBits = 5L;
// 鏁版嵁涓績鏍囪瘑浣嶆暟
private final static long datacenterIdBits = 5L;
// 鏈哄櫒ID鏈�澶у��
private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);
// 鏁版嵁涓績ID鏈�澶у��
private final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
// 姣鍐呰嚜澧炰綅
private final static long sequenceBits = 12L;
// 鏈哄櫒ID鍋忓乏绉�12浣�
private final static long workerIdShift = sequenceBits;
// 鏁版嵁涓績ID宸︾Щ17浣�
private final static long datacenterIdShift = sequenceBits + workerIdBits;
// 鏃堕棿姣宸︾Щ22浣�
private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
private final static long sequenceMask = -1L ^ (-1L << sequenceBits);
/* 涓婃鐢熶骇id鏃堕棿鎴� */
private static long lastTimestamp = -1L;
// 0锛屽苟鍙戞帶鍒�
private long sequence = 0L;
private final long workerId;
// 鏁版嵁鏍囪瘑id閮ㄥ垎
private final long datacenterId;
public IdWorker()
{
this.datacenterId = getDatacenterId(maxDatacenterId);
this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);
}
/**
* @param workerId 宸ヤ綔鏈哄櫒ID
* @param datacenterId 搴忓垪鍙�
*/
public IdWorker(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;
}
/**
* 鑾峰彇涓嬩竴涓狪D
*
* @return
*/
public synchronized long nextId()
{
long timestamp = timeGen();
if (timestamp < lastTimestamp)
{
throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
}
if (lastTimestamp == timestamp)
{
// 褰撳墠姣鍐咃紝鍒�+1
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0)
{
// 褰撳墠姣鍐呰鏁版弧浜嗭紝鍒欑瓑寰呬笅涓�绉�
timestamp = tilNextMillis(lastTimestamp);
}
}
else
{
sequence = 0L;
}
lastTimestamp = timestamp;
// ID鍋忕Щ缁勫悎鐢熸垚鏈�缁堢殑ID锛屽苟杩斿洖ID
long nextId = ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence;
return nextId;
}
private long tilNextMillis(final long lastTimestamp)
{
long timestamp = this.timeGen();
while (timestamp <= lastTimestamp)
{
timestamp = this.timeGen();
}
return timestamp;
}
private long timeGen()
{
return System.currentTimeMillis();
}
/**
* <p>
* 鑾峰彇 maxWorkerId
* </p>
*/
protected static long getMaxWorkerId(long datacenterId, long maxWorkerId)
{
StringBuffer mpid = new StringBuffer();
mpid.append(datacenterId);
String name = ManagementFactory.getRuntimeMXBean().getName();
if (!name.isEmpty())
{
/*
* GET jvmPid
*/
mpid.append(name.split("@")[0]);
}
/*
* MAC + PID 鐨� hashcode 鑾峰彇16涓綆浣�
*/
return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
}
/**
* <p>
* 鏁版嵁鏍囪瘑id閮ㄥ垎
* </p>
*/
protected static long getDatacenterId(long maxDatacenterId)
{
long id = 0L;
try
{
InetAddress ip = InetAddress.getLocalHost();
NetworkInterface network = NetworkInterface.getByInetAddress(ip);
if (network == null)
{
id = 1L;
}
else
{
byte[] mac = network.getHardwareAddress();
id = ((0x000000FF & (long) mac[mac.length - 1]) | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;
id = id % (maxDatacenterId + 1);
}
} catch (Exception e)
{
System.out.println(" getDatacenterId: " + e.getMessage());
}
return id;
}
}