仿照twitter的snowflake写了全局唯一id的实现。
twitter的是基于毫秒的,每一个ms最大能产生4096个id,因为我们是批量取的,4096有点太小了。我们就改成秒了,一秒就是4096*1000=409w,这个就足够大了。
但是修改后测试却发现出现了负数,平时自己是没发现问题。
最后发现问题出现在int的左移上,4096*1000,意味着需要移动12+10=22位。
int一共就32位,大于等于512的时候,左移22位,第一位为1就是负数了。
一个整数或操作一个负数,还是负数。这就是问题的原因了。
可能有人还会问,我一个很大的long,或一个很小的负数int,为什么就是负数呢
我们来看看二进制的表示:
-1: 11111111111111111111111111111111
-2: 11111111111111111111111111111110
-1L: 1111111111111111111111111111111111111111111111111111111111111111
-2L: 1111111111111111111111111111111111111111111111111111111111111110
int和long或操作的时候,int要转为long。负数转为long就是前面的32位都为1了。所以第一位必为1,就是肯定是负数。
上面说的有点抽象,直接看代码吧,代码就60多行而已。
/**
* snowflake算法实现
*/
public class SnowFlake {
/**
* 起始的时间戳(单位: SECONDS),2018年9月20日
*/
private final static long START = 1537372800L;
/**
* 每一部分占用的位数
*/
private final static long SEQUENCE_BIT = 22; // 序列号占用的位数
private final static long MACHINE_BIT = 10; // 机器标识占用的位数
/**
* 每一部分的最大值
*/
private final static long MAX_SEQUENCE = (1 << SEQUENCE_BIT) - 1;
/**
* 每一部分向左的位移
*/
private final static long MACHINE_LEFT = SEQUENCE_BIT;
private final static long TIME_LEFT = SEQUENCE_BIT + MACHINE_BIT;
private long sequence = 0L; // 序列号
private long lastTime = -1L;// 上一次时间戳
/**
* 产生下一个ID
* machineId必须为long。因为int在大于等于512左移22位,就会变成负数。
*/
public synchronized List<Long> nextId(long machineId, int batchSize) {
List<Long> result = new ArrayList<>(batchSize);
long currentTime = System.currentTimeMillis() / 1000;
if (currentTime < lastTime) {
throw new RuntimeException("Clock moved backwards. Refusing to generate id");
}
if (currentTime == lastTime && (sequence + batchSize) > MAX_SEQUENCE) {
// 1秒钟400多万应该是足够了的,还是超过就直接抛异常
throw new RuntimeException("SnowFlake over " + MAX_SEQUENCE + ",at time " + currentTime);
}
if (currentTime > lastTime) {
sequence = 0L;
}
long first = (currentTime - START) << TIME_LEFT // 时间戳部分
| machineId << MACHINE_LEFT // 机器标识部分
| (sequence + 1); // 序列号部分
result.add(first);
for (int i = 1; i < batchSize; i++) {
result.add(first + i);
}
sequence = sequence + batchSize;
lastTime = currentTime;
return result;
}