在上一篇博文(JDK自带UUID的性能问题)中我提到的一个满足我们需求,并能代替UUID的分布式ID的方案。在这里我做了简单的实现。
实现如下:
8位用作机器名
50位用作timestamp
6位用作标识符
用8位表示机器,可以表示256台机器。
50位表示的timestamp(毫秒),一年31536000000毫秒,可以表示35702年。
6位标识符,标识符只有在同一时间点上有两次ID的请求时会用到,用来区别这两个ID.6位表示64,也就是说只要你请求ID的TPS<64000,就不会有问题。
经过我简单的测试,这个实现在单线程下性能要比JDK自带UUID好一个数量级。在多线程下就更不用说了,因为我这个实现没有用同步。不过这两个本身也没有可比性。
import java.util.concurrent.atomic.AtomicLong;
/**
* This class is used for generating unique id in a distributed environment.
* It require a server identifier. Server identifier is a 0-255 integer.
* DistributedID(64 bits) = ServerID(8) + Time(50) + clk_seq(6)
* @author "Peter Lu"
*/
public class DistributedID {
/**
* The last time value. Used to remove duplicate IDs.
*/
private final static AtomicLong lastTime = new AtomicLong(Long.MIN_VALUE);
/**
* generate distributed ID.
*
* @param serverIdentifier support 256 servers. 0-255
* @return
*/
public static String getDistributedID(int serverIdentifier) {
long dentifier = (long)serverIdentifier << 56;
long distributedID = dentifier | genTimeBasedID();
String id = Long.toHexString(distributedID);
while(id.length()<16) {
id = "0" + id;
}
return id;
}
/**
* Generates a new time field. Each time field is unique and larger than the
* previously generated time field.
* @return
*/
private static long genTimeBasedID() {
return genTimeBasedID(System.currentTimeMillis());
}
/**
* generate a new time field from the given timestamp. Note that even identical
* values of <code>time</code> will produce different time fields.
* @param time
* @return
*/
private static long genTimeBasedID(long time) {
// 6 bits for clk_seq
time = time << 6;
// try again if compareAndSet failed
while(true) {
long tempTime = lastTime.get();
if(time>tempTime) {
if(lastTime.compareAndSet(tempTime, time)) {
break;
}
} else {
// +1 to make sure the timestamp field is different
//even the time is identical.
if(lastTime.compareAndSet(tempTime, ++tempTime)) {
time = tempTime;
break;
}
}
}
return time;
}
}