方法一:Redis实现
前言
利用Redis特有的原子性的特性,在Redis中实现自增。
在Redis中设置数据的自动递增,同时设置数据的到期时间,在业务流程中,单号是每天自动递增的,同时加上一些特有的前缀组成。
本文设计的单号的格式为:YSD20221111000066
准备
在项目中集成Redis,此处不再做过多叙述,直接进入正题!!!!!
步骤
- 获取次日凌晨时间戳
public long getNextDay() {
Calendar c = Calendar.getInstance();
c.set(Calendar.HOUR_OF_DAY, 0);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
//今天凌晨
return c.getTimeInMillis() / 1000 * (24 * 60 * 60);
}
- 生成单号的完整代码
@Component
public class RedisCode {
@Autowired
private RedisTemplate redisTemplate;
/**
* 依赖Redis的原子性,设置数据中的自动递增,有效期到12点
*
* @param key
*/
public String getCode(String key) {
StringBuilder orderNo = new StringBuilder();
String format = new SimpleDateFormat("yyyyMMdd").format(new Date());
orderNo.append("YSD").append(format);
RedisAtomicLong entityIdCounter = new RedisAtomicLong(key, Objects.requireNonNull(redisTemplate.getConnectionFactory()));
//当缓存中没有时,默认时0
long increment = entityIdCounter.getAndIncrement();
if (increment == 0) {
//当数据为0的时候,设置有效期,10秒后过期
//entityIdCounter.expire(3600, TimeUnit.SECONDS);
//设置到什么时间过期.例:次日凌晨过期
entityIdCounter.expireAt(new Date(getNextDay()));
}
//当日返回的id最低六位
increment = increment + 1;
String count = increment <= 9 ? "00000" : increment <= 99 ? "0000" : increment <= 999 ? "000" : increment <= 9999 ? "00" : increment <= 99999 ? "0" : "";
orderNo.append(count).append(increment);
return orderNo.toString();
}
/**
* 获取次日凌晨的时间戳
*
* @return
*/
public long getNextDay() {
Calendar c = Calendar.getInstance();
c.set(Calendar.HOUR_OF_DAY, 0);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
//今天凌晨
return c.getTimeInMillis() / 1000 * (24 * 60 * 60);
}
}
测试
@Test
public void d() {
long a = System.currentTimeMillis();
for (int i = 0; i < 20; i++) {
System.out.println(redisCode.getCode("code"));
}
long b = System.currentTimeMillis();
System.out.println("耗时:" + (b - a) / 1000);
}
测试结果
方法二: 雪花算法
雪花算法简介
SnowFlake 雪花算法
SnowFlake 中文意思为雪花,故称为雪花算法。最早是 Twitter 公司在其内部用于分布式环境下生成唯一 ID。在2014年开源 scala 语言版本。
如需了解更多可以自行百度,不再赘述,直接上代码。
完整代码
public class SnowflakeIdWorker {
//开始时间截 (2015-01-01)
private final long START_TIME_STAMP = 1420041600000L;
/**
* 每一部分占用的位数
*/
//序列号占用的位数
private final long SEQUENCE_BIT = 12L;
//机器标识占用的位数
private final long MACHINE_BIT = 5L;
//数据中心占用的位数
private final long DATACENTER_BIT = 5L;
/**
* 每一部分的最大值
*/
//最大数据中心数量,结果是31
private final long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);
//最大机器数量,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
private final long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
//最大序列,这里为4095 (0b111111111111=0xfff=4095)
private final long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);
/**
* 每一部分向左的位移
*/
//机器ID向左移12位
private final long MACHINE_ID_LEFT = SEQUENCE_BIT;
//数据中心id向左移17位(12+5)
private final long DATACENTER_ID_LEFT = SEQUENCE_BIT + MACHINE_BIT;
//时间截向左移22位(5+5+12)
private final long TIME_STAMP_LEFT = SEQUENCE_BIT + MACHINE_BIT + DATACENTER_BIT;
//数据中心ID(0~31)
private long datacenterId;
//机器ID(0~31)
private long machineId;
//序列号 { 毫秒内序列(0~4095)}
private long sequence = 0L;
//上一次时间戳
private long lastTimestamp = -1L;
/**
* 构造函数
*
* @param machineId 工作ID (0~31)
* @param datacenterId 数据中心ID (0~31)
*/
public SnowflakeIdWorker(long machineId, long datacenterId) {
if (machineId > MAX_MACHINE_NUM || machineId < 0) {
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", MAX_MACHINE_NUM));
}
if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {
throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", MAX_DATACENTER_NUM));
}
this.machineId = machineId;
this.datacenterId = datacenterId;
}
/**
* 获得下一个ID (该方法是线程安全的)
*
* @return SnowflakeId
*/
public synchronized long nextId() {
long currentTimeStamp = getCurrentTimeStamp();
//如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
if (currentTimeStamp < lastTimestamp) {
throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - currentTimeStamp));
}
//如果是同一时间生成的,则进行毫秒内序列
if (currentTimeStamp == lastTimestamp) {
//相同毫秒内,序列号自增
sequence = (sequence + 1) & MAX_SEQUENCE;
//毫秒内序列溢出
if (sequence == 0) {
//阻塞到下一个毫秒,获得新的时间戳
currentTimeStamp = getNewTimeStamp(lastTimestamp);
}
}
//时间戳改变,毫秒内序列重置
else {
sequence = 0L;
}
//上次生成ID的时间截
lastTimestamp = currentTimeStamp;
//移位并通过或运算拼到一起组成64位的ID
return ((currentTimeStamp - START_TIME_STAMP) << TIME_STAMP_LEFT) //时间戳部分
| (datacenterId << DATACENTER_ID_LEFT) //数据中心部分
| (machineId << MACHINE_ID_LEFT) //机器标识部分
| sequence; //序列号部分
}
/**
* 返回以毫秒为单位的当前时间
*/
protected long getCurrentTimeStamp() {
return System.currentTimeMillis();
}
/**
* 获得新的时间戳
*
* @param lastTimestamp 上次生成ID的时间截
*/
protected long getNewTimeStamp(long lastTimestamp) {
long timestamp = getCurrentTimeStamp();
while (timestamp <= lastTimestamp) {
timestamp = getCurrentTimeStamp();
}
return timestamp;
}
}
测试
@Test
public void c() {
SnowflakeIdWorker idWorker = new SnowflakeIdWorker(1, 2);
for (int i = 0; i < 10; i++) {
long id = idWorker.nextId();
System.out.println(id);
}
}
测试结果
1040665789449113606
1040665789453307904
1040665789453307905