在系统中,我们需要为每个资源设置一个唯一ID,单表时代,使用数据库的自增ID可以很简单的达到我们的目的,但是在分布式系统、多库多表的情况下,数据库自增ID就不灵了,因此,我们需要另一种算法来实现分布式环境下的唯一ID。
Snowflake核心算法:
把时间戳、工作id和序列号拼装起来,生成一个64bit的唯一id,符号位不用,其他三组bit可以根据业务需要来进行调整,默认方案下,能使用69年,部署1023台机器,每毫秒能产生4095个自增ID。
这里的69年,1023台机器,4095个自增ID是怎么算出来的的?
假设41个bit都是1,那么转换成10进制就是 2**41-1 = 2199023255552毫秒≈69年,机器数量、自增ID数量也是如此计算。
生成唯一ID的方式很多,为什么是Snowflake?
1、相对有序,数据库插入性能好。
2、Long 型存储,相对于guid uuid占用空间小。
JAVA 实现:
public final class Snowflake {
private static final Long workerIdBits = 10L;
private static final Long maxWorkerId = -1L ^ (-1L << workerIdBits);
private static final Long sequenceBits = 12L;
private static final Long maxSequence = -1L ^ (-1L << sequenceBits);
private static final Long workerIdShift = sequenceBits;
private static final Long timestampLeftShift = sequenceBits + workerIdBits;
private final SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmssSSS");
private final Long workerId;
private Long sequence = 0L;
private Long lastTimestamp = -1L;
private Long since = 0l;
public Snowflake(final Long workerId) {
super();
if ((workerId > this.maxWorkerId) || (workerId < 0)) {
throw new IllegalArgumentException(String.format(
"worker Id can't be greater than %d or less than 0",
this.maxWorkerId));
}
this.workerId = workerId;
Calendar calendar = Calendar.getInstance();
calendar.set(2017, Calendar.NOVEMBER, 3, 0, 0, 0);
this.since = calendar.getTimeInMillis();
}
public synchronized Long nextId() {
long timestamp = this.timeGen();
if (this.lastTimestamp == timestamp) {
this.sequence = (this.sequence + 1) & this.maxSequence;
if (this.sequence == 0) {
timestamp = this.tilNextMillis(this.lastTimestamp);
}
} else {
this.sequence = 0L;
}
if (timestamp < this.lastTimestamp) {
throw new RuntimeException(
String.format(
"Clock moved backwards. Refusing to generate id for %d milliseconds",
this.lastTimestamp - timestamp));
}
this.lastTimestamp = timestamp;
return ((timestamp - since) << timestampLeftShift) | (this.workerId << this.workerIdShift) | (this.sequence);
}
/**
* 生成
* prefix 长度最多3个字符
* 返回的字符串长度最多26个字符
*
* @param prefix
* @return
*/
public synchronized String nextSerialNumber(String prefix) {
prefix = StringUtils.trimToEmpty(prefix);
if (prefix.length() > 3) throw new BusinessException("prefix长度不能大于3", Error.ILLEGAL_ARGUMENT);
long timestamp = this.timeGen();
if (this.lastTimestamp == timestamp) {
this.sequence = (this.sequence + 1) & this.maxSequence;
if (this.sequence == 0) {
timestamp = this.tilNextMillis(this.lastTimestamp);
}
} else {
this.sequence = 0L;
}
if (timestamp < this.lastTimestamp) {
throw new RuntimeException(
String.format(
"Clock moved backwards. Refusing to generate id for %d milliseconds",
this.lastTimestamp - timestamp));
}
this.lastTimestamp = timestamp;
String time = format.format(new Date());
return String.format("%s%s%s%6d", prefix, time, workerId, this.sequence);
}
private long tilNextMillis(final long lastTimestamp) {
long timestamp = this.timeGen();
while (timestamp <= lastTimestamp) {
timestamp = this.timeGen();
}
return timestamp;
}
private long timeGen() {
return System.currentTimeMillis();
}
}
py3实现
# -*- coding: utf-8 -*-
import time
WORKER_ID_BITS = 10
MAX_WORK_ID = -1^(-1<<WORKER_ID_BITS)
SEQUENCE_BITS = 12
MAX_SEQUENCE = -1^(-1<<SEQUENCE_BITS)
TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS+WORKER_ID_BITS
WORKER_ID_SHIFT = SEQUENCE_BITS
class Snowflake(object):
def __init__(self,workerId):
self.workerId = workerId
self.sequence = 0
self.lastTimestamp = -1
self.since = 0
def nextId(self):
timestamp = self._timestamp()
if self.lastTimestamp == timestamp:
self.sequence = (self.sequence + 1) & MAX_SEQUENCE
if self.sequence == 0:
timestamp = self._tilNextMillis()
else:
self.sequence = 0
self.lastTimestamp = timestamp
return (timestamp - self.since) << TIMESTAMP_LEFT_SHIFT | self.workerId << WORKER_ID_SHIFT | self.sequence
def _timestamp(self):
return int(round(time.time()*1000))
def _tilNextMillis(self):
timestamp=self._timestamp()
while timestamp<self.lastTimestamp:
timestamp = self._timestamp()
return timestamp
参考:
1. https://github.com/twitter/snowflake
2. http://www.cnblogs.com/relucent/p/4955340.html
3.http://www.lanindex.com/twitter-snowflake%EF%BC%8C64%E4%BD%8D%E8%87%AA%E5%A2%9Eid%E7%AE%97%E6%B3%95%E8%AF%A6%E8%A7%A3/
来源:oschina
链接:https://my.oschina.net/u/1023800/blog/1535269