账号生成算法
相信大家都有过如何生成账号的困惑,今天我来说一下我初学的时候常用的账号生成算法。
- 随机数
- mysql自增主键
- 雪花算法
随机数
当时刚刚学习JavaSE,想的是直接随机数生成id,不知道mysql主键可以进行自增。
简单来说,随机生成id会有以下缺点:
- 每次生成一个id,需要去mysql中查询是否有重复的,这样数据量巨大的时候便会效率及其低下。
- 分布式环境下若代码比较烂可能会产生线程安全问题。
所以随机数是最笨的生成全局唯一id的方式。
mysql自增
我在之后的项目中使用mysql自增进行id的生成,但是久而久之我也发现了一些问题,问题如下:
- 主键直接暴露,可能会有安全问题。
- 主键不应该与任何业务逻辑有关。
mysql在分布式环境下也不合适。
推特开源的雪花算法
雪花算法其实我觉得没有绝对标准的版本,因为他可以根据自己的需要进行修改。
现在来讲述网络上传播最广的雪花算法的原理:
雪花算法生成的是一个64位的id,其中分为5个部分:
- 1bit的固定正号(保持id为正)
- 41bit的时间戳(用时间保持id自增)
- 10bit的工作ID(机器id与机房id)
- 12bit的序列号(本地计数器)
public class Snowflake {
//机器ID
private Long workId;
//机房ID
private Long datacenterId;
//一毫秒生成的多个id的最新序号
private Long sequence;
//时间初始值
private Long iniTime = 1585644268888L;
//5位机器ID
private Long workerIdBits = 5L;
//5位机房ID
private Long datacenterIdBits = 5L;
//每毫秒产生id数的2的12次方
private Long sequenceBits = 12L;
//最大机器ID
private Long maxWorkerId = -1L ^ (-1L << workerIdBits);
//最大机房ID
private Long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
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 Snowflake(Long workerId, Long datacenterId, Long sequence) {
if (workerId > maxWorkerId || maxWorkerId < 0) {
throw new IllegalArgumentException(
String.format("机器ID不能超过%d和小于0", maxWorkerId)
);
}
if (datacenterId > maxDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException(
String.format("机房ID不能超过%d和小于0", maxDatacenterId)
);
}
this.workId = workerId;
this.datacenterId = datacenterId;
this.sequence = sequence;
}
//生成唯一ID
public synchronized Long nextId() {
Long timeStamp = System.currentTimeMillis();
if (timeStamp < lastTimeStamp) {
System.err.printf("错误!,时间已被回调");
throw new RuntimeException(
String.format("时间已被回调%d秒", lastTimeStamp - timeStamp)
);
}
//如果在同一毫秒
if (lastTimeStamp.equals(timeStamp)) {
sequence = (sequence+1)&sequenceMask;
if(sequence==0){
timeStamp = tilNextMillis(lastTimeStamp);
}
}else{
sequence = 0L;
}
lastTimeStamp = timeStamp;
return ((timeStamp-iniTime)<<timeStampLeftShift)|(datacenterId<<datacenterIdShift)|(workId<<workerIdShift)|sequence;
}
private Long tilNextMillis(Long lastTimeStamp){
Long now = System.currentTimeMillis();
while(now<=lastTimeStamp){
now=System.currentTimeMillis();
}
return now;
}
}
雪花算法的优缺点
优点:
- 思路简单
- 高性能
- 低延迟
缺点:
- 需要人部署
总结
其实分布式唯一ID生成算法有很多,包括雪花算法可以自己修改,主要是理解将时间戳与本地计时器保持唯一ID的思想。