实用向—总结一些唯一ID生成方式

Redis Incr 命令会将 key 中储存的数字值增一。如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。

这里以jedis为例提供两种自增ID的生成方式

第一种方式直接通过Incr命令获取自增ID

JedisUtils.incr("inc-test1")

第二张方式获取带有时间戳的唯一编码,时间细度为分钟

/**
     * 基于Redis生成 时间+递增ID 的唯一编码
     * @param key
     * @return
     */
    public static String getRedisTimeSeq(String key) {

        String time = DateUtils.parseDateToStr("yyyyMMddHHmm",new Date());

        StringBuilder sBuilder = new StringBuilder(time);

        sBuilder.append(JedisUtils.incr(key+":"+time,120));//保证一分钟内KEY有效

        return sBuilder.toString();
    }

二、测试

下面我们从冲突率与时间性能上,对以上几种唯一ID的生成方式进行一个简单的测试,同时基于并发安全的考虑,测试分为单线程与多线程两种

// ---------------测试---------------
    public static void main(String[] args) throws InterruptedException {

        int length = 10000000;
        SnowFlake snowFlake = new SnowFlake(1, 1);

        final CountDownLatch countDownLatch = new CountDownLatch(10);

        Map map = new ConcurrentHashMap();
        long begin = System.currentTimeMillis();
        for(int i=0;i) {
            Thread thread = new Thread(new Runnable() {
                public void run() {
                    for (int i = 0; i != 1000000; ++i) {

                        String str = String.valueOf(snowFlake.nextId());

//                        String str = StringUtils.getUUIDHashCode(); //根据UUID产生命令唯一标识  长度 32  字母数字组合
//
//                        String str = StringUtils.getOrderSeq();//根据UUID取hash值+随机数,产生命令唯一标识 长度 16位纯数字
//
//                        String str =StringUtils.getRandomHexString(); //长度16的16进制字符串

                        map.put(str, str);

                    }
                    countDownLatch.countDown();

                }
            });

            thread.start();
        }

        countDownLatch.await();

        System.out.println("冲突数为: " + (length - map.size()));
        System.out.println("sync time: " + (System.currentTimeMillis() - begin));

        Map map1 = new ConcurrentHashMap();
        begin = System.currentTimeMillis();
        for (int i = 0; i != length; ++i) {

            String str = String.valueOf(snowFlake.nextId());

//            String str = StringUtils.getUUIDHashCode();//根据UUID产生命令唯一标识

//            String str = StringUtils.getOrderSeq();//根据UUID取hash值+随机数,产生命令唯一标识

//            String str =StringUtils.getRandomHexString();

            map1.put(str, str);

        }
        System.out.println("冲突数为: " + (length - map1.size()));
        System.out.println("sync time: " + (System.currentTimeMillis() - begin));
    }

测试结果如下:

生成方式 生成总数 并发 冲突数 耗时

UUID产生命令唯一标识

1000W 单线程 0 26166ms

UUID产生命令唯一标识

1000W 多线程 0 27915ms

根据UUID取hash值+随机数,产生命令唯一标识

1000W 单线程 0 25405ms

根据UUID取hash值+随机数,产生命令唯一标识

1000W 多线程 0 25023ms

十六位随机的十六进制字符串

1000W 单线程 0 25723ms

十六位随机的十六进制字符串

1000W 多线程 0 28094ms

雪花算法

1000W 单线程 0 10100ms

雪花算法

1000W 多线程 0 11713ms

针对 Redis Incr 命令进行了本地和局域网两种测试, 由于千万级数据耗时太长,数据量改为了百万级,结果如下:

生成方式 网络环境 生成总数 并发 冲突数 耗时 Redis Incr命令获取自增ID 本地 100W 单线程 0 72445ms Redis Incr命令获取自增ID 本地 100W 多线程 0 47879ms Redis Incr命令获取自增ID 局域网 100W 单线程 0 71447ms Redis Incr命令获取自增ID 局域网 100W 多线程 0 45888ms

Redis Incr命令生成 时间+递增ID 的唯一编码

局域网 100W 单线程 0 236795ms

Redis Incr命令生成 时间+递增ID 的唯一编码

局域网 100W 多线程 0 39281ms

可以看到Redis相比前面一些轻量级的ID生成方式,生成效率上有明显差距,但在分布式环境下,且业务场景对全局唯一ID的生成样式有要求, redis做为统一的ID生成器还是很有必要的。

由于测试受机器配置、网络带宽等条件影响,以上得出的结果只是一个简单的测试结果,证明这几种唯一ID生成方式具备一定的可用性,大家如果有兴趣可以进行更深入的测试与优化;

三、总结

其实在日常开发中唯一ID的的生成方式与整体服务的架构与复杂度是密切相关的,本文从并发安全、冲突率、性能等多个方面列举了几种唯一ID的生成方式,相对比较简单实用,但在更复杂的架构与业务场景下,对唯一ID生成的方式的考量就需要更加全面,如并发量、持久化、获取方式等,都需要具体问题具体分析,这里不再深入探讨,希望本文对大家能有所帮助,其中如有不足与不正确的地方还望指出与海涵。

关注微信公众号,查看更多技术文章。

实用向—总结一些唯一ID生成方式

Original: https://www.cnblogs.com/dafanjoy/p/13690888.html
Author: DaFanJoy
Title: 实用向—总结一些唯一ID生成方式

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/715077/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球