标签:
在分布式系统中,经常需要使用全局唯一ID
查找对应的数据。产生这种ID需要保证系统全局唯一,而且要高性能以及占用相对较少的空间。
全局唯一ID在数据库中一般会被设成主键,这样为了保证数据插入时索引的快速建立,还需要保持一个有序的趋势。
这样全局唯一ID就需要保证这两个需求:
当服务使用的数据库只有单库单表时,可以利用数据库的auto_increment
来生成全局唯一递增ID.
优势:
劣势:
一般的语言中会自带UUID的实现,比如Java中UUID方式UUID.randomUUID().toString()
,可以通过服务程序本地产生,ID的生成不依赖数据库的实现。
优势:
劣势:
snowflake
是twitter开源的分布式ID生成算法,其核心思想是:产生一个long型的ID,使用其中41bit作为毫秒数,10bit作为机器编号,12bit作为毫秒内序列号。这个算法单机每秒内理论上最多可以生成1000*(2^12)
个,也就是大约400W
的ID,完全能满足业务的需求。
根据snowflake
算法的思想,我们可以根据自己的业务场景,产生自己的全局唯一ID。因为Java中long
类型的长度是64bits,所以我们设计的ID需要控制在64bits。
比如我们设计的ID包含以下信息:
| 41 bits: Timestamp | 3 bits: 区域 | 10 bits: 机器编号 | 10 bits: 序列号 |
产生唯一ID的Java
代码:
import java.security.SecureRandom;
/**
* 自定义 ID 生成器
* ID 生成规则: ID长达 64 bits
*
* | 41 bits: Timestamp (毫秒) | 3 bits: 区域(机房) | 10 bits: 机器编号 | 10 bits: 序列号 |
*/
public class CustomUUID {
// 基准时间
private long twepoch = 1288834974657L; //Thu, 04 Nov 2010 01:42:54 GMT
// 区域标志位数
private final static long regionIdBits = 3L;
// 机器标识位数
private final static long workerIdBits = 10L;
// 序列号识位数
private final static long sequenceBits = 10L;
// 区域标志ID最大值
private final static long maxRegionId = -1L ^ (-1L << regionIdBits);
// 机器ID最大值
private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);
// 序列号ID最大值
private final static long sequenceMask = -1L ^ (-1L << sequenceBits);
// 机器ID偏左移10位
private final static long workerIdShift = sequenceBits;
// 业务ID偏左移20位
private final static long regionIdShift = sequenceBits + workerIdBits;
// 时间毫秒左移23位
private final static long timestampLeftShift = sequenceBits + workerIdBits + regionIdBits;
private static long lastTimestamp = -1L;
private long sequence = 0L;
private final long workerId;
private final long regionId;
public CustomUUID(long workerId, long regionId) {
// 如果超出范围就抛出异常
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException("worker Id can‘t be greater than %d or less than 0");
}
if (regionId > maxRegionId || regionId < 0) {
throw new IllegalArgumentException("datacenter Id can‘t be greater than %d or less than 0");
}
this.workerId = workerId;
this.regionId = regionId;
}
public CustomUUID(long workerId) {
// 如果超出范围就抛出异常
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException("worker Id can‘t be greater than %d or less than 0");
}
this.workerId = workerId;
this.regionId = 0;
}
public long generate