当前位置: 移动技术网 > IT编程>开发语言>Java > 分布式UUID的生成

分布式UUID的生成

2018年11月16日  | 移动技术网IT编程  | 我要评论

女艾滋病的早期症状,成飞中学学生,齐齐哈尔论坛

1.通过jdk标准api?uuid会重复

要生成uuid,大多会直接使用下面这句:

uuid.randomuuid().tostring().replace("-", "");

在多数情况,这样的处理是没问题的,毕竟是jdk标准接口。但是在某些情况下,会出现重复。搜素 uuid 重复,就会发现有人踩到了雷

先看uuid各版本的实现原理:universally unique identifier

再看jdk的实现(只实现了uuid的1,3,4版本)java.util.uuid

会发现在分布式场景下jdk自带的这个工具类并不好用。原因:

  • 会存在多台web容器在同1个物理/云主机上,mac地址相同。因此,版本1的uuid,不合适
  • randomuuid实现的是uuid的版本4,产生重复的概率是可以计算出来的,海量存储时,重复不可避免。这也是有人踩雷的原因
  • nameuuidfrombytes实现的是uuid的版本3,保证种子的唯一性才能确保生成的uuid唯一。在分布式的场景下,如果我们每次都能获取到唯一的种子,那也就不必用这个方法生成uuid了

2.数据库获取uuid?性能不能保证

通过这种消耗大量性能来获取uuid,当然可行,但在高并发的场景下你真的会去考虑吗?

3.分布式uuid的生成

分布式?多台web容器(我们可以称之为实例)在同1个机器(mac地址相同)下?不依赖第3方工具?最好在jvm解决?

思路

  • 确保每台实例具有唯一的名字(我们可以称之为实例名)

  • 确保某台实例生成的uuid不会重复: 当前系统时间 + 递增的数值(避免高并发的影响)

因此,算法如下:

uuid = 实例名 + 当前系统时间毫秒数 + 递增的int数

方法

  1. 对每台web容器的java_options配置不一样的实例名

    以tomcat(8.0.53)为例,在startup.bat里配置:

    rem to set java_opts
    set "java_opts=%java_opts% -dinstance.name=cico-mba"

    这样,上文的instance.name,就变成了jvm里的1个参数了

  2. 代码实现

    package java.main;
    
    import java.util.concurrent.atomic.atomicinteger;
    
    public class uuidutil {
    
        /* 从当前web容器的java_options中,获取jvm的配置:当前实例名 */
        private static final string instance_name = system.getproperty("instance.name");
        /* 实例名脱敏后的值 */
        private static string instance_name_by_num = null;
        /* 计数器。atomicinteger是java.util.concurrent下的类,jdk的算法工程师会避免并发问题 */
        private static atomicinteger cnt = new atomicinteger(0);
    
        /**
         * 初始化instance_name_by_num。需考虑并发
         */
        private synchronized static void initinstancenamebynum() {
            if (null != instance_name_by_num) {
                return;
            }
            char[] chars = instance_name.tochararray();
            stringbuilder sb = new stringbuilder();
            for (char c : chars) {
                sb.append((int) c);
            }
            instance_name_by_num = sb.tostring();
        }
    
        /**
         * 生成分布式的uuid
         * 
         * @return
         */
        public static string getconcurrentuuid() {
            if (null == instance_name) {
                return "the jvm option is null, named 'instance.name'";
            }
            if (null == instance_name_by_num) {
                initinstancenamebynum();
            }
            stringbuilder uuid = new stringbuilder();
            uuid.append(instance_name_by_num);
            uuid.append(system.currenttimemillis());
            uuid.append(cnt.incrementandget());
            return uuid.tostring();
        }
    }   

说明

通过上文的方法可在jvm内快速生成支持分布式的uuid。这个uuid的长度,由下面3部分组成:

  • 13: system.currenttimemillis()的长度是13位
  • 11: integer.min_value的长度。int值从0开始递增,达到int的上限后,会从负数开始重新计数,因此长度最大是11位
  • 2 * 实例名的字符数: 实例名一般由字母、数字、小数点、减号、下划线组成,这些字符的ascii码值是2位

如果这个uuid需要持久化,持久化的字段可定义成varchar2(255),其中实例名的字符长度最大可以是115 = ( 255 - 13 - 11 ) / 2

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网