/// 根据twitter的snowflake算法生成唯一ID
/// snowflake算法 64 位
/// 0---0000000000 0000000000 0000000000 0000000000 0 --- 00000 ---00000 ---000000000000
/// 第一位为未使用(实际上也可作为long的符号位),接下来的41位为毫秒级时间,然后5位datacenter标识位,5位机器ID(并不算标识符,实际是为线程标识),然后12位该毫秒内的当前毫秒内的计数,加起来刚好64位,为一个Long型。
/// 其中datacenter标识位起始是机器位,机器ID其实是线程标识,可以同一一个10位来表示不同机器
///
public class IdWorker
//机器ID
private static long workerId = 1;
private static long twepoch = 687888001020L; //唯一时间,这是一个避免重复的随机量,自行设定不要大于当前时间戳
private static long sequence = 0L;
private static int workerIdBits = 4; //机器码字节数。4个字节用来保存机器码
public static long maxWorkerId = -1L ^ -1L << workerIdBits; //最大机器ID
private static int sequenceBits = 10; //计数器字节数,10个字节用来保存计数码
private static int workerIdShift = sequenceBits; //机器码数据左移位数,就是后面计数器占用的位数
private static int timestampLeftShift = sequenceBits + workerIdBits; //时间戳左移动位数就是机器码和计数器总字节数
public static long sequenceMask = -1L ^ -1L << sequenceBits; //一微秒内可以产生计数,如果达到该值则等到下一微妙在进行生成
private long lastTimestamp = -1L;
public long nextId()
lock (this)
long timestamp = timeGen();
if (this.lastTimestamp == timestamp)
{ //同一微妙中生成ID
IdWorker.sequence = (IdWorker.sequence + 1) & IdWorker.sequenceMask; //用&运算计算该微秒内产生的计数是否已经到达上限
if (IdWorker.sequence == 0)
//一微妙内产生的ID计数已达上限,等待下一微妙
timestamp = tillNextMillis(this.lastTimestamp);
{ //不同微秒生成ID
IdWorker.sequence = 0; //计数清0
if (timestamp < lastTimestamp)
{ //如果当前时间戳比上一次生成ID时时间戳还小,抛出异常,因为不能保证现在生成的ID之前没有生成过
throw new Exception(string.Format("Clock moved backwards. Refusing to generate id for {0} milliseconds",
this.lastTimestamp - timestamp));
this.lastTimestamp = timestamp; //把当前时间戳保存为最后生成ID的时间戳
long nextId = (timestamp - twepoch << timestampLeftShift) | IdWorker.workerId << IdWorker.workerIdShift | IdWorker.sequence;
return nextId;
///
/// 获取下一微秒时间戳
///
///
///
private long tillNextMillis(long lastTimestamp)
long timestamp = timeGen();
while (timestamp <= lastTimestamp)
timestamp = timeGen();
return timestamp;
///
/// 生成当前时间戳
///
///
private long timeGen()
return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
public class IdWorker {
protected static final Logger LOG = LoggerFactory.getLogger(IdWorker.class);
private long workerId;
private long datacenterId;
private long sequence = 0L;
private long twepoch = 1288834974657L;
private long workerIdBits = 5L;
private long datacenterIdBits = 5L;
private long maxWorkerId = -1L ^ (-1L << workerIdBits);
private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
private long sequenceBits = 12L;
private long workerIdShift = sequenceBits;
private long datacenterIdShift = sequenceBits + workerIdBits;
private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
private long sequenceMask = -1L ^ (-1L << sequenceBits);
private long lastTimestamp = -1L;
public IdWorker(long workerId, long datacenterId) {
// sanity check for workerId
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
if (datacenterId > maxDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
this.workerId = workerId;
this.datacenterId = datacenterId;
LOG.info(String.format("worker starting. timestamp left shift %d, datacenter id bits %d, worker id bits %d, sequence bits %d, workerid %d", timestampLeftShift, datacenterIdBits, workerIdBits, sequenceBits, workerId));
public synchronized long nextId() {
long timestamp = timeGen();
if (timestamp < lastTimestamp) {
LOG.error(String.format("clock is moving backwards. Rejecting requests until %d.", lastTimestamp));
throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
} else {
sequence = 0L;
lastTimestamp = timestamp;
return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence;
protected long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
return timestamp;
protected long timeGen() {
return System.currentTimeMillis();
在我们的工作中,数据库某些表的字段会用到唯一的,趋势递增的订单编号,我们将介绍两种方法,一种是传统的采用随机数生成的方式,另外一种是采用当前比较流行的“分布式唯一ID生成算法-雪花算法”来实现。
一、时间戳随机数生成唯一ID
我们写一个for循环,用
RandomUtil.generateOrderCode()生成1000个唯一ID,执行结果我们会发现出现重复的ID。
分布式ID生成方案选型!详细解析雪花算法Snowflake
本篇文章中和大家分享了分布式项目中的全局唯一ID生成算法的雪花算法。首先从分布式项目中的ID需要满足的条件引入问题,然后详细解析了雪花算法Snowflake生成唯一ID的具体方式,最后通过一个具体实例SnowflakeIdWorker介绍了雪花算法的具体使用。
分布式唯一ID系列(5)——Twitter的雪法算法Snowflake适合做分布式ID吗
写到这里,分布式Id算是写到最后一篇了,在这一篇里,我会讲到目前网上最适合分布式Id的方法,什么方法呢,请您往下看:
介绍Snowflake算法
SnowFlake算法是国际大公司Twitter的采用的一种生成分布式自增id的策略,这个算法产生的分布式id是足够我们我们中小公司在日常里面的使用了。
世界上没有两片雪花是完全相同的。
为了满足Twitter每秒上万条消息的请求,每条消息都必须分配一条唯一的ID,这些id还需要一些大致的顺序(方便客户端排序),并且在分布式系统中不同机器产生的ID必须不同。