简介
Snowflake 是 Twitter 开源的分布式 ID 生成算法,目的是在分布式系统中生成全局唯一且递增的 long(64bit) 的id数值。
其特性是各节点无需协调、按时间升序、且整个集群各节点id不重复。
该数值的默认组成如下(符号位之外的三部分允许个性化调整):
1bit: 符号位,始终是 0(为了保证主键是正数)。
41bit: 毫秒数(可以使用69年);
10bit: 工作机器id(支持1024个节点)
12bit: 序列号(每个节点每毫秒内支持生成4096个ID)
整个64位数值最大值为2的63次方-1,正好等于java中的Long.MAX_VALUE
实战
1.初始化具体的Snowflake类
@Service
@Qualifier("snowflakeService")
public class SnowflakeService {
@Value("${dataCenterId}")
private Long dataCenterId;
@Value("${workerId}")
private Long workerId;
private ConcurrentHashMap<String, Snowflake> hashMap = new ConcurrentHashMap<>();
public Snowflake snowflake(String table, String column) {
String key = String.format("%s-%s", table, column);
return hashMap.computeIfAbsent(key, it -> new Snowflake(dataCenterId, workerId));
}
}
computeIfAbsent()方法是判断一个map中是否存在这个key,如果存在则返回value的对象,如果不存在,则创建一个满足要求的对象存储到value中。
2.实现GenId接口
public class SnowflakeGenId implements GenId<Long> {
@Override
public Long genId(String table, String column) {
SnowflakeService service = SpringContextUtils.getBean("snowflakeService",
SnowflakeService.class);
return service.snowflake(table, column).nextId();
}
}
3.在entity的主键属性上设置注解
@Id
@KeySql(genId = SnowflakeGenId.class)
private Long id;
这样的话,在insert的时候就会根据配置的方法生成id
Snowflake原理
public synchronized long nextId() {
//获取当前时间戳
long currTimestamp = timestampGen();
//如果当前时间戳小于上一次生成id的时间戳,抛出异常
if (currTimestamp < lastTimestamp) {
throw new IllegalStateException(
String.format("Clock moved backwards. Refusing to generate id for %d milliseconds",
lastTimestamp - currTimestamp));
}
//如果时间戳相等,则通过生成自增的序列号来区分id
if (currTimestamp == lastTimestamp) {
//将序列号+1
sequence = (sequence + 1) & maxSequence;
//如果序列号已达到最大值,则等到下一毫秒在生成
if (sequence == 0) { // overflow: greater than max sequence
currTimestamp = waitNextMillis(currTimestamp);
}
}
//如果时间戳不相等,则重置序列号为0
else {
sequence = 0L;
}
//记录生成的时间戳
lastTimestamp = currTimestamp;
return ((currTimestamp - EPOCH) << timestampShift) | //时间戳部分
(datacenterId << datacenterIdShift) | //数据中心标识部分
(workerId << workerIdShift) | //机器标识部分
sequence; //序列号部分
}