网上很多全局唯一ID的生成策略,感兴趣的可以去百度看看,自己写的一个基于reids生成id的一个ID要求就是:唯一性、安全性=》高可用、高性能、递增性
等
一、分析
- 特性:
2.规则
- 总结:
二、代码
package com.hmdp.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
/**
* 基于redis全局唯一ID生成器
* 优点:1.唯一(不必说了)
* 2.单调递增(天然排序、分页优势)
* 4.不依赖与关系型数据库,灵活方便。
* 3.sql索引的查询效率,long类型的值比String的查询效率高。
* @author TH
* @date 2022/3/24
*/
@Component
public class RedisIdGenerator {
/**
* 自定义的一个开始时间戳2022-01-05 00:00:00
*/
private final static long BEGIN_TIMESTAMP=1641340800L;
/**
* 步长/变化数量:一般情况集群有多台redis那么有几台步长就设置多少
*/
private final static long DELTA=1L;
/**
*序列号的一个位数。
*/
private final static int COUNT_BITS=32;
/**
* 时间格式
*/
private final static String PATTERN="yyyy:MM:dd";
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* 获取下一个ID值
* @param keyPrefix
* @return
*/
public long nextId(String keyPrefix){
//1.高位值,获取当前系统时间与指定时间的时间戳差值
LocalDateTime now = LocalDateTime.now();
long nowTime = now.toEpochSecond(ZoneOffset.UTC);
long nowTimeStamp = nowTime - BEGIN_TIMESTAMP;
//2.低位值
//2.1获取key的时间规则
String sTime = now.format(DateTimeFormatter.ofPattern(PATTERN));
//2.2 获取redis的单点自增值,如果key不存在,那么会新增一个key作为起始值。1个redis的时候方式一
//long endTimesTamp = stringRedisTemplate.opsForValue().increment("idIncr:" + keyPrefix + ":" + sTime);
//2.2 如果是集群下有多台redis那么有几台步长就设置多少。
long endTimesTamp = stringRedisTemplate.opsForValue().increment("idIncr:" + keyPrefix + ":" + sTime,DELTA);
//3.拼接返回,数值的一种拼接方式,把时间戳移动到高位,位运算、或运算。
return nowTimeStamp << COUNT_BITS | endTimesTamp;
}
public static void main(String[] args) {
//设置指定日期2222-01-05 00:00:00
LocalDateTime dateTime = LocalDateTime.of(2022, 1, 5, 0, 0, 0);
//获取秒的时间戳,UTC-现世界标准时间
long second = dateTime.toEpochSecond(ZoneOffset.UTC);
System.out.println("second="+second);
}
}
2.测试代码:
private ExecutorService es= Executors.newFixedThreadPool(10);
/**
* 测试id生成器:模拟20000个ID并发生成
*/
@Test
void testIdGenerator() throws InterruptedException {
CountDownLatch downLatch=new CountDownLatch(200);
Runnable task=()->{
for (int i = 0; i < 100; i++) {
long orderId = redisIdGenerator.nextId("orderId");
System.out.println("id="+orderId);
}
downLatch.countDown();
};
long begin=System.currentTimeMillis();
for (int i = 0; i < 200; i++) {
es.submit(task);
}
downLatch.await();
long end=System.currentTimeMillis();
//花费时常会比实际生成id的时间稍微长一点点。毕竟多了一些打印啊什么的。
System.out.println("time="+(end-begin));
}