零、本文纲要
- 一、全局唯一ID
- 二、Redis生成全局唯一ID
1、snowflake算法全局唯一ID策略
2、Redis自增全局唯一ID策略 - 三、代码实现
tips:Ctrl + F快速定位所需内容阅读吧。
一、全局唯一ID
1、全局唯一ID特点
- ① 全局唯一性
- ② 高性能
- ③ 单调递增
- ④ 信息安全
- ⑤ 高可用
2、全局唯一ID生成策略
- ① UUID
- ② Redis自增
- ③ snowflake算法
- ④ 数据库自增
- ⑤ Zookeeper的znode版本生成ID
二、Redis生成全局唯一ID
1、snowflake算法全局唯一ID策略
此处我们参考snowflake算法的ID策略
,其具体策略如下:
① 1位,固定位;
② 41位,用来记录时间戳(毫秒),接近69年;
③ 10位,用来记录工作机器id;
④ 12位,序列号,用来记录同毫秒内产生的不同id;
2、Redis自增全局唯一ID策略
自定义我们自己的Redis自增的ID策略
,具体如下:
① 1位,固定位;
② 31位,用来记录时间戳(秒),接近69年;
③ 32位,序列号,用来记录同一秒内产生的不同id;
三、代码实现
1、获取开始时间戳
LocalDateTime#of(int year, int month, int dayOfMonth, int hour, int minute, int second)
方法,传入自己需要的起始年月日时分秒;
public static void main(String[] args) {
LocalDateTime time = LocalDateTime.of(2022,1,1,0,0,0);
long l = time.toEpochSecond(ZoneOffset.UTC);
System.out.println(l);
}
如:2022年1月1日 00点00分00秒,为1640995200L
。
2、编写ID生成工具类
@Component注解
,将工具类注册到Spring容器当中,方便使用;
BEGIN_TIMESTAMP
自定义起始的时间戳;
COUNT_BITS
位移量,后续拼接全局唯一ID时使用;
@Component
public class RedisIdWorker {
// 开始时间戳
private static final long BEGIN_TIMESTAMP = 1640995200L;
// 位移量
private static final long COUNT_BITS = 32L;
private StringRedisTemplate stringRedisTemplate;
public RedisIdWorker(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
}
public long nextId(String keyPrefix){...}
}
timestamp
获取当前设置起始时间戳的偏移量;
拼接key
,例如:incr:order:20220325;
位运算拼接全局ID,timestamp << COUNT_BITS | count;
;
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 currentDate = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
//2.2 自增长
// 拼接的key,例如:incr:order:20220325
long count = stringRedisTemplate.opsForValue().increment(
RedisConstants.INCR_ID
+ keyPrefix
+ RedisConstants.SEPARATE
+ currentDate);
//3. 拼接并返回
return timestamp << COUNT_BITS | count;
}
3、测试生成全局唯一ID
注入RedisIdWorker对象,用于测试;
@Resource
private RedisIdWorker redisIdWorker;
编写测试方法:
① 使用线程池
模拟并发调用,此处简单处理;
private ExecutorService es = Executors.newFixedThreadPool(500);
② 使用CountDownLatch
控制线程执行;
CountDownLatch latch = new CountDownLatch(300); //设置300阈值
Runnable task = () -> {
...
latch.countDown();//每次循环结束,latch -1
};
... //开始时间
... //执行任务
latch.await();//等待直到 latch 变为 0
... //结束时间
完整的测试方法如下:
@Test
public void testIDWorker() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(300);
Runnable task = () -> {
for (int i = 0; i < 100; i++) {
long id = redisIdWorker.nextId("order");
System.out.println(id);
}
latch.countDown();
};
long begin = System.currentTimeMillis();
for (int i = 0; i < 300; i++) {
es.submit(task);
}
latch.await();
long end = System.currentTimeMillis();
System.out.println("time = " + (end - begin));
}
四、结尾
以上即为Redis全局唯一ID的基础内容,感谢阅读。