java如何根据时间戳生成有序ID

 更新时间:2024年04月15日 10:01:57   作者:紫金丨小飞侠  
这篇文章主要介绍了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使用itext填充pdf表单及导出pdf的流程

    SpringBoot使用itext填充pdf表单及导出pdf的流程

    由于最近开发的项目需要用到打印单据,就在网上找了一下方案,反反复复,都没有找到合适的,借鉴了网上资源,使用itext5、itext7的工具包,所以本文介绍了SpringBoot使用itext填充pdf表单及导出pdf的流程,需要的朋友可以参考下
    2024-09-09
  • Java分布式session存储解决方案图解

    Java分布式session存储解决方案图解

    这篇文章主要介绍了Java分布式session存储解决方案图解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-07-07
  • 详解java定时任务

    详解java定时任务

    这篇文章主要为大家详细介绍了java定时任务,使用JDK中的Timer定时任务来实现,感兴趣的小伙伴们可以参考一下
    2016-03-03
  • Java多线程案例实战之定时器的实现

    Java多线程案例实战之定时器的实现

    在Java中可以使用多线程和定时器来实现定时任务,下面这篇文章主要给大家介绍了关于Java多线程案例之定时器实现的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-01-01
  • Java预防SQL注入的具体实践方法

    Java预防SQL注入的具体实践方法

    在 Java 中预防 SQL 注入的核心是 避免直接拼接 SQL 语句,并通过参数化查询、ORM 框架和严格的输入验证来实现安全防护,以下是具体实践方法,感兴趣的小伙伴跟着小编一起来看看吧
    2025-01-01
  • 将InputStream转化为base64的实例

    将InputStream转化为base64的实例

    这篇文章主要介绍了将InputStream转化为base64的实例,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • SpringBoot启动失败的原因及其解决方法

    SpringBoot启动失败的原因及其解决方法

    对于springboot的启动失败,相信大家都有经历,但是为什么会启动失败,以及怎么解决都只能通过日志进行查看,在这里,我会将常见的springboot启动失败的报错一一展示,需要的朋友可以参考下
    2024-06-06
  • Java设计模式之观察者模式(Observer模式)介绍

    Java设计模式之观察者模式(Observer模式)介绍

    这篇文章主要介绍了Java设计模式之观察者模式(Observer模式)介绍,Java深入到一定程度,就不可避免的碰到设计模式(design pattern)这一概念,了解设计模式,将使自己对java中的接口或抽象类应用有更深的理解,需要的朋友可以参考下
    2015-03-03
  • Springboot+redis+Interceptor+自定义annotation实现接口自动幂等

    Springboot+redis+Interceptor+自定义annotation实现接口自动幂等

    本篇文章给大家介绍了使用springboot和拦截器、redis来优雅的实现接口幂等,对于幂等在实际的开发过程中是十分重要的,因为一个接口可能会被无数的客户端调用,如何保证其不影响后台的业务处理,如何保证其只影响数据一次是非常重要的,感兴趣的朋友跟随小编一起看看吧
    2019-07-07
  • 详解如何使用MyBatis简化JDBC开发

    详解如何使用MyBatis简化JDBC开发

    JavaEE 企业级 Java 项目中的经典三层架构为表现层,业务层和持久层.MyBatis 对 JDBC 代码进行了封装,作为一款优秀的持久层框架,专门用于简化JDBC开发.本文主要介绍一下如何使用MyBatis简化JDBC开发,需要的可以参考一下
    2023-01-01

最新评论