1. 全局ID生成器
id的规律性明显造成某些信息的泄露;
使用自增ID作为主键会导致一些问题。首先,由于自增ID必须是唯一的,因此当达到最大值时,无法再向表中插入新的数据,这限制了表的数据量。例如:订单如果一直自增,id值达到数亿级别时,单表就很难插入了;
其次,由于每次插入新数据都需要更新自增ID的值,当表中存在大量数据时,这个过程会变得越来越耗时;
-
全局唯一性:生成的ID在整个分布式系统中必须是唯一的,避免ID冲突的发生。这是保证数据一致性和避免数据丢失的关键。
-
高性能:生成ID的速度应该足够快,以满足高并发的请求。高性能的ID生成器能够处理大量的请求,而不会成为系统的瓶颈。
-
递增性:生成的ID应该具备趋势递增的特性,即后续生成的ID值应该大于前面生成的ID值。这可以提高数据库索引的性能,减少IO的次数。
-
高可用:ID生成器应该是可靠可用的,即保证在系统故障或网络中断的情况下,依然能够正常生成唯一ID。
-
安全性:生成的ID应该是难以被猜测到且具有权限控制机制,还有不包括敏感信息。
系列号就是Redis自增的数值
2. Redis实现全局ID
要点:
在对序列号进行自增长时,为防止自增值过大采用拼接时间戳date;
最后得到全局ID,时间戳左移与序列号做或运算得到全局ID,思路值得学习;
@Component
public class RedisIdWorker {
private StringRedisTemplate stringRedisTemplate;
public RedisIdWorker(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
}
/**
* 开始时间戳
*/
private static final long BEGIN_TIMESTAMP = 1640995200L;
/**
* 序列号位数
*/
private static final int COUNT_BITS = 32;
public long nextId(String keyPrefix){
//1. 生成时间戳
LocalDateTime now = LocalDateTime.now();
long nowEpochSecond = now.toEpochSecond(ZoneOffset.UTC);
long timeStamp = nowEpochSecond - 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. 拼接并返回(时间戳左移与序列号做或运算得到全局ID)
return timeStamp << COUNT_BITS | count;
}
}
对生成ID策略进行测试;
要点:
lambda表达式的应用;
多线程运用以及线程池的应用,关于计算多线程的运算时间的方法;
new CountDownLatch(300),设置初始计数值为300;
调用await()方法进行等待,直到计数器值为0,才会继续执行后续代码,从而得出运算的时间;
通过使用CountDownLatch来等待所有任务执行完毕,并使用线程池来并发执行任务,评估redisIdWorker的性能;
Runnable task = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
long id = redisIdWorker.nextId("order");
System.out.println("id= " + id);
}
}
};
@Resource
private RedisIdWorker redisIdWorker;
/**
* 创建线程池
*/
private ExecutorService ex = Executors.newFixedThreadPool(500);
@Test
void testIdWorker() throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(300);
//lambda表达式
Runnable task = () -> {
for (int i = 0; i < 100; i++) {
long id = redisIdWorker.nextId("order");
System.out.println("id= " + id);
}
countDownLatch.countDown();
};
long begin = System.currentTimeMillis();
for (int i = 0; i < 300; i++) {
ex.submit(task);
}
countDownLatch.await();
long end = System.currentTimeMillis();
System.out.println("time = " + (end - begin));
}