V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
yuhuan66666
V2EX  ›  Java

Java 生成的 UUID 出现了重复?! 什么鬼??

  •  
  •   yuhuan66666 · 2017-05-22 13:13:17 +08:00 · 21616 次点击
    这是一个创建于 2772 天前的主题,其中的信息可能已经有所发展或是发生改变。

    不是号称 比被陨石砸中都的概率还小么。。。。。

    怎么当一年的主键就重复了好几个呢。。。。

    各位大大 都用啥当主键的?

    难不成真要 各个字段连成一条+UUID 生成的字符串,再算成 SHA512 当主键吗???

    太复杂了吧。。。。

    第 1 条附言  ·  2017-05-22 14:09:51 +08:00
    用的就是 UUID.randomUUID()方法生成的 UUID
    第 2 条附言  ·  2017-05-22 15:58:38 +08:00
    请问使用 java 怎样可以生成一个时间基于时间的唯一性 UUID 呢 我现在用的全是版本 4 的
    27 条回复    2017-07-12 00:10:20 +08:00
    knightdf
        1
    knightdf  
       2017-05-22 13:28:48 +08:00
    没人说不重复啊。。。
    lonenol
        2
    lonenol  
       2017-05-22 13:30:33 +08:00
    各种算法一大堆,随便 Goolebaidubing 一下
    congeec
        3
    congeec  
       2017-05-22 13:34:18 +08:00
    出现重复的就二次 hash 呗
    oh
        4
    oh  
       2017-05-22 13:34:33 +08:00 via iPhone
    好想知道楼主的业务一年业务量是多少,出现了好几条重复
    watzds
        5
    watzds  
       2017-05-22 13:36:48 +08:00 via Android
    也可能代码有 bug
    wu1990
        6
    wu1990  
       2017-05-22 13:38:14 +08:00
    数据量多大
    zjsxwc
        7
    zjsxwc  
       2017-05-22 13:38:33 +08:00
    只能相对地说不会重复,具体还是要看使用环境:
    https://zhihu.com/question/34876910/answer/88924223
    honeycomb
        8
    honeycomb  
       2017-05-22 13:38:49 +08:00 via Android
    你是生成了哪种 UUID ?
    类型 1 还是类型 4 ?

    类型 4 是全随机的,要考虑随机数生成器的用法有没有问题。
    类型 1 和时间有关,每秒钟有 1630 亿个可用。
    nanpuyue
        9
    nanpuyue  
       2017-05-22 13:39:04 +08:00   ❤️ 3
    楼主你出门可要小心天上的陨石了~
    tomczhen
        11
    tomczhen  
       2017-05-22 13:48:27 +08:00 via iPhone
    uuid 也分好几种的,而且多线程并行生成不用一些方法的话也有生成相同 uuid 的可能。
    bozong
        12
    bozong  
       2017-05-22 13:56:35 +08:00
    Tb 级别数据量?
    ytmsdy
        13
    ytmsdy  
       2017-05-22 14:01:22 +08:00
    快去买彩票!
    yuhuan66666
        14
    yuhuan66666  
    OP
       2017-05-22 14:10:11 +08:00
    @honeycomb #8 用的最简单的 UUID.randomUUID()生成的
    icedx
        15
    icedx  
       2017-05-22 14:11:39 +08:00
    和时间戳绑定一下就好了
    xmh51
        16
    xmh51  
       2017-05-22 14:22:35 +08:00
    多线程 下 使用?
    jason19659
        17
    jason19659  
       2017-05-22 14:35:12 +08:00
    找个按照时间戳生成的工具类
    angelface
        18
    angelface  
       2017-05-22 14:41:41 +08:00
    @yuhuan66666 Type 4 类的是随机数,是有可能重复的。可以换一个 Time-Based 的工具包。
    magicdawn
        19
    magicdawn  
       2017-05-22 14:43:38 +08:00
    ooTwToo
        20
    ooTwToo  
       2017-05-22 14:53:55 +08:00
    试试 twitter 的 snowflake
    honeycomb
        21
    honeycomb  
       2017-05-22 16:16:36 +08:00
    用版本 1 就可以了

    看到一个类似例子:
    http://bbs.csdn.net/topics/390434666

    按理说 getUUID 背后用的是 UUID 类里一个静态 SecureRandom,随机数的来源是 SecureRandom.nextBytes,不该出现重复问题
    yuhuan66666
        22
    yuhuan66666  
    OP
       2017-05-22 16:53:00 +08:00
    @honeycomb #21 请问 怎么才能生成版本 1 的 UUID 呢 我看 java API 没提供对应的方法
    momocraft
        23
    momocraft  
       2017-05-22 17:22:27 +08:00
    @honeycomb "Secure" 的要求是无法预测。在长度有限时要求不重复是不现实的。
    superadmin
        24
    superadmin  
       2017-05-22 17:41:46 +08:00
    Twitter-Snowflake
    参考:
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;

    /**
    * tweeter 的 snowflake 移植到 Java:
    * (a) id 构成: 42 位的时间前缀 + 10 位的节点标识 + 12 位的 sequence 避免并发的数字(12 位不够用时强制得到新的时间前缀)
    * 注意这里进行了小改动: snowkflake 是 5 位的 datacenter 加 5 位的机器 id; 这里变成使用 10 位的机器 id
    * (b) 对系统时间的依赖性非常强,需关闭 ntp 的时间同步功能。当检测到 ntp 时间调整后,将会拒绝分配 id
    */

    public class IdWorker {

    private final static Logger logger = LoggerFactory.getLogger(IdWorker.class);

    private final long workerId;
    private final long epoch = 1403854494756L; // 时间起始标记点,作为基准,一般取系统的最近时间
    private final long workerIdBits = 10L; // 机器标识位数
    private final long maxWorkerId = -1L ^ -1L << this.workerIdBits;// 机器 ID 最大值: 1023
    private long sequence = 0L; // 0,并发控制
    private final long sequenceBits = 12L; //毫秒内自增位

    private final long workerIdShift = this.sequenceBits; // 12
    private final long timestampLeftShift = this.sequenceBits + this.workerIdBits;// 22
    private final long sequenceMask = -1L ^ -1L << this.sequenceBits; // 4095,111111111111,12 位
    private long lastTimestamp = -1L;

    private IdWorker(long workerId) {
    if (workerId > this.maxWorkerId || workerId < 0) {
    throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", this.maxWorkerId));
    }
    this.workerId = workerId;
    }

    public synchronized long nextId() throws Exception {
    long timestamp = this.timeGen();
    if (this.lastTimestamp == timestamp) { // 如果上一个 timestamp 与新产生的相等,则 sequence 加一(0-4095 循环); 对新的 timestamp,sequence 从 0 开始
    this.sequence = this.sequence + 1 & this.sequenceMask;
    if (this.sequence == 0) {
    timestamp = this.tilNextMillis(this.lastTimestamp);// 重新生成 timestamp
    }
    } else {
    this.sequence = 0;
    }

    if (timestamp < this.lastTimestamp) {
    logger.error(String.format("clock moved backwards.Refusing to generate id for %d milliseconds", (this.lastTimestamp - timestamp)));
    throw new Exception(String.format("clock moved backwards.Refusing to generate id for %d milliseconds", (this.lastTimestamp - timestamp)));
    }

    this.lastTimestamp = timestamp;
    return timestamp - this.epoch << this.timestampLeftShift | this.workerId << this.workerIdShift | this.sequence;
    }

    private static IdWorker flowIdWorker = new IdWorker(1);
    public static IdWorker getFlowIdWorkerInstance() {
    return flowIdWorker;
    }



    /**
    * 等待下一个毫秒的到来, 保证返回的毫秒数在参数 lastTimestamp 之后
    */
    private long tilNextMillis(long lastTimestamp) {
    long timestamp = this.timeGen();
    while (timestamp <= lastTimestamp) {
    timestamp = this.timeGen();
    }
    return timestamp;
    }

    /**
    * 获得系统当前毫秒数
    */
    private static long timeGen() {
    return System.currentTimeMillis();
    }

    public static void main(String[] args) throws Exception {
    System.out.println(timeGen());

    IdWorker idWorker = IdWorker.getFlowIdWorkerInstance();
    // System.out.println(Long.toBinaryString(idWorker.nextId()));
    System.out.println(idWorker.nextId());
    System.out.println(idWorker.nextId());
    }

    }
    aksoft
        25
    aksoft  
       2017-05-23 08:32:14 +08:00
    啥事也没有绝对的
    julyclyde
        26
    julyclyde  
       2017-05-23 14:58:15 +08:00
    只要数量有限,那将来必然重复
    无非你比别人早点碰上了而已,有啥好大惊小怪的
    xufeng
        27
    xufeng  
       2017-07-12 00:10:20 +08:00
    重复的概率确实有,如果你的数据量不大,那么应该考虑你的代码是否有问题。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5450 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 07:22 · PVG 15:22 · LAX 23:22 · JFK 02:22
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.