java如何根据时间戳生成有序ID
引言
我们常用的主键有这么几种
1. 数据库自增主键,比如mysql的autoincrement,这种插入快,但是识别度不高
2. uuid 这个号称是全球唯一的,但是无序,没有实际意义,只能保证唯一
3. 时间戳,这种在分布式的场景下就需要考虑更多种情况
4. 雪花算法 snow flake ,分布式全局唯一主键,很牛,但是我觉得用起来也挺麻烦哈哈哈
所以在并发情况没那么大的时候用一个工具类搞定,我就是这么懒
工具类
@Slf4j public class NumUtil { private static long tmpID = 0; private static final long LOCK_TIME = 1; private static final long INCREASE_STEP = 1; private static SimpleDateFormat sdf = new SimpleDateFormat("yyMMddHHmmssSSS"); private static final Lock LOCK = new ReentrantLock(); public static long nextPkId() throws InterruptedException { //当前:(年、月、日、时、分、秒、毫秒) long timeCount; if (LOCK.tryLock(LOCK_TIME, TimeUnit.SECONDS)) { timeCount = Long.parseLong(sdf.format(new Date())); try { if (tmpID < timeCount) { tmpID = timeCount; } else { tmpID += INCREASE_STEP; timeCount = tmpID; } return timeCount; } finally { LOCK.unlock(); } } else { log.error("lock failed"); return nextPkId(); } } }
贴上代码,这里用了当前时间,精确到毫秒级,如果有需要的话可以在实例化timeCount的时候乘以10或者100 1000之类的,这个看大家,然后加上锁,防止线程不安全的情况,加锁失败的时候递归,再来一次。
也可以使用synchronized做成同步方法,当中的区别下次再讨论。
有评论说宕机会导致tmpID归0导致已经使用过超出当前时间的ID,所以持久化这个tmpID也是可以的。
但这也就是在并发没那么高的情况下才使用这种方法,一般并发场景下还是分布式锁+推特的雪花算法解决。
测试
public static void numTest() { ExecutorService executorService = Executors.newCachedThreadPool(); int n = 10000; List<Long> list = new ArrayList<>(); CountDownLatch latch = new CountDownLatch(n); for (int i = 0; i < n; i++) { executorService.execute(() -> { //执行业务请求 try { list.add(NumUtil.nextPkId()); } catch (InterruptedException e) { e.printStackTrace(); } latch.countDown(); }); } try { // 一定记得加上timeout时间,防止阻塞主线程 latch.await(3000, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { log.error(e.getMessage()); } //4.等待所有子任务完成,组装内容 while (list.size() < n) { log.info("集合长度 >>> {}",list.size()); } //5.关闭线程池 executorService.shutdown(); for (Long aLong : list) { System.out.println(aLong); } }
然后噼里啪啦打印了一万个ID,没有重复的,一秒以内生成
结论
当然这种只是为了单体或者是并发没有高到那么离谱的场景下使用,效率我觉得还不错,分布式的场景下可能需要用到redis的自增来计数以达到数据安全的效果
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家
相关文章
SpringBoot Starter自定义全局加解密组件的详细流程
SpringBoot Starter作用将一组相关的依赖打包,简化项目的配置和初始化过程,通过特定的Starter开发者可以快速的实现特定功能模块的开发和扩展,本文给大家介绍了SpringBoot Starter自定义全局加解密组件的详细流程,需要的朋友可以参考下2024-02-02关于idea引入spring boot <parent></parent>父依赖标红问题
这篇文章主要介绍了idea引入spring boot <parent></parent>父依赖标红问题,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下2020-10-10
最新评论