分布式ID生成器
使用UUID:缺点:太长,且没有顺序。
使用Redis:缺点:Redis压力大,以及额外网络开销。
使用分布式ID生成器:是由程序来生成唯一的主键的值。(本文使用)
snowflake(雪花)算法
public class IdWorker {
private final static long TWEPOCH = 1288834974657 L;
private final static long WORKER_ID_BITS = 5 L;
private final static long DATACENTER_ID_BITS = 5 L;
private final static long MAX_WORKER_ID = - 1 L ^ ( - 1 L << WORKER_ID_BITS) ;
private final static long MAX_DATACENTER_ID = - 1 L ^ ( - 1 L << DATACENTER_ID_BITS) ;
private final static long SEQUENCE_BITS = 12 L;
private final static long WORKER_ID_SHIFT = SEQUENCE_BITS;
private final static long DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
private final static long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATACENTER_ID_BITS;
private final static long SEQUENCE_MASK = - 1 L ^ ( - 1 L << SEQUENCE_BITS) ;
private static long lastTimestamp = - 1 L;
private long sequence = 0 L;
private final long workerId;
private final long datacenterId;
public IdWorker ( ) {
this . datacenterId = getDatacenterId ( MAX_DATACENTER_ID) ;
this . workerId = getMaxWorkerId ( datacenterId, MAX_WORKER_ID) ;
}
public IdWorker ( long workerId, long datacenterId) {
if ( workerId > MAX_WORKER_ID || workerId < 0 ) {
throw new IllegalArgumentException ( String. format ( "worker Id can't be greater than %d or less than 0" , MAX_WORKER_ID) ) ;
}
if ( datacenterId > MAX_DATACENTER_ID || datacenterId < 0 ) {
throw new IllegalArgumentException ( String. format ( "datacenter Id can't be greater than %d or less than 0" , MAX_DATACENTER_ID) ) ;
}
this . workerId = workerId;
this . datacenterId = datacenterId;
}
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) {
sequence = ( sequence + 1 ) & SEQUENCE_MASK;
if ( sequence == 0 ) {
timestamp = tilNextMillis ( lastTimestamp) ;
}
} else {
sequence = 0 L;
}
lastTimestamp = timestamp;
long nextId = ( ( timestamp - TWEPOCH) << TIMESTAMP_LEFT_SHIFT)
| ( datacenterId << DATACENTER_ID_SHIFT)
| ( workerId << WORKER_ID_SHIFT) | 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 ( ) ;
}
protected static long getMaxWorkerId ( long datacenterId, long maxWorkerId) {
StringBuffer mpid = new StringBuffer ( ) ;
mpid. append ( datacenterId) ;
String name = ManagementFactory. getRuntimeMXBean ( ) . getName ( ) ;
if ( ! name. isEmpty ( ) ) {
mpid. append ( name. split ( "@" ) [ 0 ] ) ;
}
return ( mpid. toString ( ) . hashCode ( ) & 0xffff ) % ( maxWorkerId + 1 ) ;
}
protected static long getDatacenterId ( long maxDatacenterId) {
long id = 0 L;
try {
InetAddress ip = InetAddress. getLocalHost ( ) ;
NetworkInterface network = NetworkInterface. getByInetAddress ( ip) ;
if ( network == null) {
id = 1 L;
} 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;
}
}
测试
public class IdWorkerTest {
@Test
public void nextId ( ) {
IdWorker idWorker = new IdWorker ( ) ;
for ( int i = 0 ; i < 100 ; i++ ) {
System. out. println ( idWorker. nextId ( ) ) ;
}
}
}
输出
1164145902732673024
1164145902736867328
1164145902736867329
1164145902736867330
1164145902736867331
1164145902736867332
1164145902736867333
1164145902736867334
1164145902736867335
1164145902736867336