ID是一个唯一标识,在业务开发中,我们要保证ID是唯一的,不容易猜测的。
在数据库中,对于mybatis-plus,它自带了雪花算法生成id的工具包来为我们使用,并生成唯一id
雪花算法的生成策略是这样的:
采用一个64位bit long型来生成唯一id
那我们如果没用导入这样一个工具包,让我们自己写的话,我们又如何保证唯一性呢?保证他的可用性呢?保证它的不可猜测性呢?
有人会说,用自增啊!这样每次都+1每个数据的id肯定是不一样的。这样好像说的对,但是这样如果数据量一大,你用long型,最多也就2^64个不一样的数,平常可能够用,但对大项目来说,这显然有点不够了,同时,这种数又容易被猜测,知道了一个,下一个也就知道了,不安全,所以,这种方案适合小业务,同时又不是很重要的业务来使用。
既然我们不导这样的工具包,那我们自己写一个这样的工具包,总可以吧!
由于机器码服务码这些有点难搞,那我们就用时间戳和自增数拼接成一个唯一id。
时间总是不一样的,id也是不一样的。这样就拼接好了一个唯一id。
这时有人就说了,那自增也会用完啊!这时,我们可以用redis自增,然后每天用的key不同(拼接日期),这样,一天的业务量肯定是够的。
好有了思路,我们开始设计具体步骤:
1.获取时间戳
设置初始时间,然后每天时间-初始时间,得到时间戳
2.生成序列号
通过标识+日期得到key,然后拿到自增数
3.拼接
通过位运算,将时间戳左移32位,然后或运算拼接
@Component
public class RedisIdWorker {
/**
* 开始时间戳
*/
private static final long BEGIN_TIMESTAMP = 1640995200L;
/**
* 序列号的位数
*/
private static final int COUNT_BITS = 32;
private StringRedisTemplate stringRedisTemplate;
public RedisIdWorker(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
}
public long nextId(String keyPrefix) {
// 1.生成时间戳
LocalDateTime now = LocalDateTime.now();
long nowSecond = now.toEpochSecond(ZoneOffset.UTC);
long timestamp = nowSecond - BEGIN_TIMESTAMP;
// 2.生成序列号
// 2.1.获取当前日期,精确到天
String date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
// 2.2.自增长
long count = stringRedisTemplate.opsForValue().increment("icr:" + keyPrefix + ":" + date);
// 3.拼接并返回
return timestamp << COUNT_BITS | count;
}
}