redis全局唯一ID生成策略
一、有哪些生成全局唯一ID的策略
二、使用Redis自增
1. 分析
2. RedisIdWorker配置类
主要的逻辑就是,生成一个long类型的id,然后前31位通过时间戳来填充,后32位通过自增来填充,前31位用来判断时间,后32位是为了防止在那一秒中的时候,有成千上万的订单。
@Component
public class RedisIdWorker {
private static final long BEGIN_TIMESTAMP=1689811200;
/**
* 序列号的位数
*/
private static final int COUNT_BITS=32;
private StringRedisTemplate stringRedisTemplate;
public RedisIdWorker(StringRedisTemplate stringRedisTemplate){//可以使用构造函数来注入,也可以直接用 @Resource
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("yyyyMMdd"));
//2.2自增长
long count = stringRedisTemplate.opsForValue().increment("icr:" + keyPrefix + ":" + date);
//3.拼接返回
return timestamp<<COUNT_BITS | count;//把COUNT_BITS时间戳往前面移动32位,后面的用count去插入。|代表的意思是,你count插入的一定成功
}
/**
* 这个用来生成时间戳到目标年月日时分秒过的秒数。1689811200
*/
/* public static void main(String[] args) {//这个用来生成时间戳到当前年月日时分秒过的秒数。
LocalDateTime time =LocalDateTime.of(2023,7,20,0,0,0);
long second = time.toEpochSecond(ZoneOffset.UTC);
System.out.println(second);
}*/
}
3 单元测试+注解分析(难点较多)
对于 CountDownLatch、Lambda 的解释在代码后面
@Resource
private RedisIdWorker redisIdWorker;//注入我们的配置类
private ExecutorService es = Executors.newFixedThreadPool(500);//定义一个500的线程池
@Test
void testIdWorker() throws InterruptedException {//redis全局唯一id的策略。
CountDownLatch latch =new CountDownLatch(300); //countDownLatch相当于一个计数器,能够使一个线程等待( latch.await();)另外一些线程完成各自的工作后,
// 再继续执行。这个计数器的初始值就是线程的数量,每当一个线程完成之后,计数器就进行减1,当计数器的值为0时,
// 那么在countDownLatch上等待的线程就可以继续执行
Runnable task=()->{ //这里是一个Lambda 表达式
for (int i=0;i<100;i++){ //一次生成100个id
Long id =redisIdWorker.nextId("order");//这个就是我们定义的类里面的方法“order是别名”
System.out.println("id="+id);
}
latch.countDown();; //上面的for循环完一次,进行减一,减到0就执行下面的 latch.await();后面的操作
};
long begin =System.currentTimeMillis();
for(int i=0;i<300;i++){ //程序其实从这里才开始,我们提交300个任务到线程里面,线程提交到task这个函数
es.submit(task);
}
latch.await();//这里是当上面的latch为0后,开始启动这里
long end =System.currentTimeMillis();
System.out.println("time="+(end-begin));
}
3.1 countDownLatch前言
countDownLatch( 门阀、 计数器)是多线程控制的一种工具 ,它用来协调各个线程之间的同步。
countDownLatch相当于一个计数器,能够使一个线程等待另外一些线程完成各自的工作后,再继续执行。这个计数器的初始值就是线程的数量,每当一个线程完成之后,计数器就进行减1,当计数器的值为0时,那么在countDownLatch上等待的线程就可以继续执行。
countDownLatch接收一个int类型的参数,表示要等待的工作线程个数
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException(“count < 0”);
this.sync = new Sync(count);
}
3.2 常用方法
方法 说明
await() 使当前线程进入同步队列进行等待,直到latch的值被减到0或者当前线程被中断,当前线程就会被唤醒。
await(long timeout, TimeUnit unit) 带超时时间的await()。
countDown() 使latch的值减1,如果减到了0,则会唤醒所有等待在这个latch上的线程
getCount() 获得latch的数值
原文链接:https://blog.csdn.net/qq_50652600/article/details/128075662
Lambda的超详细总结:https://blog.csdn.net/huangjhai/article/details/107110182