什么是UUID?

UUID是Universally Unique Identifier的缩写,它是在一定的范围内(从特定的名字空间到全球)唯一的机器生成的标识符。UUID具有以下涵义:

  • 经由一定的算法机器生成

为了保证UUID的唯一性,规范定义了包括网卡MAC地址、时间戳、名字空间(Namespace)、随机或伪随机数、时序等元素,以及从这些元素生成UUID的算法。UUID的复杂特性在保证了其唯一性的同时,意味着只能由计算机生成。

  • 非人工指定,非人工识别

UUID是不能人工指定的,除非你冒着UUID重复的风险。UUID的复杂性决定了“一般人“不能直接从一个UUID知道哪个对象和它关联。

  • 在特定的范围内重复的可能性极小

UUID的生成规范定义的算法主要目的就是要保证其唯一性。但这个唯一性是有限的,只在特定的范围内才能得到保证,这和UUID的类型有关(参见UUID的版本)。

UUID是16字节128位长的数字,通常以36字节的字符串表示,示例如下:

3F2504E0-4F89-11D3-9A0C-0305E82C3301

其中的字母是16进制表示,大小写无关。

Universally Unique IDentifier(UUID),有着正儿八经的RFC规范,是一个128bit的数字,也可以表现为32个16进制的字符,中间用”-”分割。

- 时间戳+UUID版本号,分三段占16个字符(60bit+4bit),
- Clock Sequence号与保留字段,占4个字符(13bit+3bit),
- 节点标识占12个字符(48bit),

GUID(Globally Unique Identifier)是UUID的别名;但在实际应用中,GUID通常是指微软实现的UUID。

UUID的版本

UUID具有多个版本,每个版本的算法不同,应用范围也不同。

首先是一个特例--Nil UUID--通常我们不会用到它,它是由全为0的数字组成,如下:

00000000-0000-0000-0000-000000000000

UUID Version 1:基于时间的UUID

因为时间戳有满满的60bit,所以可以尽情花,以100纳秒为1,从1582年10月15日算起(能撑3655年,真是位数多给烧的,1582年有意思么)

节点标识也有48bit,一般用MAC地址表达,如果有多块网卡就随便用一块。如果没网卡,就用随机数凑数,或者拿一堆尽量多的其他的信息,比如主机名什么的,拼在一起再hash一把。

顺序号这16bit则仅用于避免前面的节点标示改变(如网卡改了),时钟系统出问题(如重启后时钟快了慢了),让它随机一下避免重复。

但好像Version 1就没考虑过一台机器上起了两个进程这类的问题,也没考虑相同时间戳的并发问题,所以严格的Version1没人实现,接着往下看各个变种吧。

Version1变种 – Hibernate

Hibernate的CustomVersionOneStrategy.java,解决了之前version 1的两个问题

- 时间戳(6bytes, 48bit):毫秒级别的,从1970年算起,能撑8925年….
- 顺序号(2bytes, 16bit, 最大值65535): 没有时间戳过了一秒要归零的事,各搞各的,short溢出到了负数就归0。
- 机器标识(4bytes 32bit): 拿localHost的IP地址,IPV4呢正好4个byte,但如果是IPV6要16个bytes,就只拿前4个byte。
- 进程标识(4bytes 32bit): 用当前时间戳右移8位再取整数应付,不信两条线程会同时启动。

值得留意就是,机器进程和进程标识组成的64bit Long几乎不变,只变动另一个Long就够了。

Version1变种 – MongoDB

MongoDB的ObjectId.java

- 时间戳(4 bytes 32bit): 是秒级别的,从1970年算起,能撑136年。

- 自增序列(3bytes 24bit, 最大值一千六百万): 是一个从随机数开始(机智)的Int不断加一,也没有时间戳过了一秒要归零的事,各搞各的。因为只有3bytes,所以一个4bytes的Int还要截一下后3bytes。

- 机器标识(3bytes 24bit): 将所有网卡的Mac地址拼在一起做个HashCode,同样一个int还要截一下后3bytes。搞不到网卡就用随机数混过去。

- 进程标识(2bytes 16bits):从JMX里搞回来到进程号,搞不到就用进程名的hash或者随机数混过去。

可见,MongoDB的每一个字段设计都比Hibernate的更合理一点,比如时间戳是秒级别的。总长度也降到了12 bytes 96bit,但如果果用64bit长的Long来保存有点不上不下的,只能表达成byte数组或16进制字符串。

另外对Java版的driver在自增序列那里好像有bug。

Twitter的snowflake派号器

snowflake也是一个派号器,基于Thrift的服务,不过不是用redis简单自增,而是类似UUID version1,

只有一个Long 64bit的长度,所以IdWorker紧巴巴的分配成:

- 时间戳(42bit) 自从2012年以来(比那些从1970年算起的会过日子)的毫秒数,能撑139年。
- 自增序列(12bit,最大值4096), 毫秒之内的自增,过了一毫秒会重新置0。
- DataCenter ID (5 bit, 最大值32),配置值。
- Worker ID ( 5 bit, 最大值32),配置值,因为是派号器的id,所以一个数据中心里最多32个派号器就够了,还会在ZK里做下注册。

可见,因为是派号器,把机器标识和进程标识都省出来了,所以能够只用一个Long表达。

另外,这种派号器,client每次只能一个ID,不能批量取,所以额外增加的延时是问题。

UUID Version 2:DCE安全的UUID

DCE(Distributed Computing Environment)安全的UUID和基于时间的UUID算法相同,但会把时间戳的前4位置换为POSIX的UID或GID。这个版本的UUID在实际中较少用到。

UUID Version 3:基于名字的UUID(MD5)

基于名字的UUID通过计算名字和名字空间的MD5散列值得到。这个版本的UUID保证了:相同名字空间中不同名字生成的UUID的唯一性;不同名字空间中的UUID的唯一性;相同名字空间中相同名字的UUID重复生成是相同的。

UUID Version 4:随机UUID

根据随机数,或者伪随机数生成UUID。这种UUID产生重复的概率是可以计算出来的,但随机的东西就像是买彩票:你指望它发财是不可能的,但狗屎运通常会在不经意中到来。

UUID Version 5:基于名字的UUID(SHA1)

和版本3的UUID算法类似,只是散列值计算使用SHA1(Secure Hash Algorithm 1)算法。

UUID的应用

从UUID的不同版本可以看出,Version 1/2适合应用于分布式计算环境下,具有高度的唯一性;Version 3/5适合于一定范围内名字唯一,且需要或可能会重复生成UUID的环境下;至于Version 4,我个人的建议是最好不用(虽然它是最简单最方便的)。

通常我们建议使用UUID来标识对象或持久化数据,但以下情况最好不使用UUID:

  • 映射类型的对象。比如只有代码及名称的代码表。
  • 人工维护的非系统生成对象。比如系统中的部分基础数据。

对于具有名称不可重复的自然特性的对象,最好使用Version 3/5的UUID。比如系统中的用户。如果用户的UUID是Version 1的,如果你不小心删除了再重建用户,你会发现人还是那个人,用户已经不是那个用户了。(虽然标记为删除状态也是一种解决方案,但会带来实现上的复杂性。)

UUID生成器

我没想着有人看完了这篇文章就去自己实现一个UUID生成器,所以前面的内容并不涉及算法的细节。下面是一些可用的Java UUID生成器:

  • Java UUID Generator (JUG):开源UUID生成器,LGPL协议,支持MAC地址。
  • UUID:特殊的License,有源码。
  • Java 5以上版本中自带的UUID生成器:好像只能生成Version 3/4的UUID。

此外,Hibernate中也有一个UUID生成器,但是,生成的不是任何一个(规范)版本的UUID,强烈不建议使用。

生成方法

搜集了一些UUID的生成方法,整理如下

Shell

  • Unix/Linux环境中大都有一个名为uuidgen的小工具,运行即可生成一个UUID到标准输出

读取文件/proc/sys/kernel/random/uuid即得UUID,例如:

cat /proc/sys/kernel/random/uuid

libuuid

libuuid是一个用于生成UUID的C库,具体用法参考http://linux.die.net/man/3/libuuid,示例如下:

#include <stdio.h>#include <uuid/uuid.h>int main(int argc, char **argv)
{uuid_t uuid;char str[36];uuid_generate(uuid);uuid_unparse(uuid, str);printf("%s\n", str);return 0;}

在Linux下编译时需要链接uuid库

gcc -o uuid uuid.c -luuid

在Ubuntu中,可以用下面的命令安装libuuid:

sudo apt-get install uuid-dev

boost uuid

Boost库是一个可移植的开源C++库,它提供了UUID的实现。

下面的代码可以生成一个UUID

#include <boost/uuid/uuid.hpp>#include <boost/uuid/uuid_generators.hpp>boost::uuids::uuid a_uuid = boost::uuids::random_generator();

Qt QUuid

Qt是一个跨平台的C++编程框架,QUuid类实现了UUID的生成、比较、转换等功能。

函数QUuid createUuid();可用于生成一个随即UUID。示例如下

#include <iostream>#include <QUuid>#include <QString>int main(){QUuid uuid = QUuid::createUuid();std::cout << qPrintable(uuid.toString()) << std::endl;return 0;}

CoCreateGuid

Windows下提供了函数CoCreateGuid用于生成GUID。需要使用的头文件是”objbase.h”,需要链接的库是ole32.lib,函数原型为:

HRESULT CoCreateGuid(GUID *pguid);

GUID的原型为

typedef struct _GUID{DWORD Data1;WORD Data2;WORD Data3;BYTE Data4[8];} GUID;

Java

JDK 1.5以上支持UUID,用法如下:

import java.util.UUID;String uuid = UUID.randomUUID().toString();

UUID介绍与生成方法相关推荐

  1. UUID介绍与生成的方法

    什么是UUID? UUID是Universally Unique Identifier的缩写,它是在一定的范围内(从特定的名字空间到全球)唯一的机器生成的标识符.UUID具有以下涵义: 经由一定的算法 ...

  2. patch文件介绍和生成方法

    Git 打补丁-- patch 和 diff 的使用(详细) git diff和diff产生的文件简介 git patch制作相关简介 git format-patch 用法 如果要修改的patch当 ...

  3. UUID/GUID介绍、生成规则及生成代码

    UUID/GUID介绍.生成规则及生成代码 1. UUID介绍 1.1 介绍 1.2 UUID优势 1.3 UUID劣势 2. UUID版本 2.1 版本1 - 基于时间的UUID 2.1.1优点 2 ...

  4. 一种非常简单的静态网页生成方法介绍

    一.目前的静态页生成方法有简单的模板替换.常见的ASP+FSO等,这里给大家介绍一种更简单的方法.原理就是借助XMLHTTP对象获取目标页面的源代码,然后写入到静态网页文件中.代码如下: Code D ...

  5. DELMIA软件:机器人仿真动画视频生成功能介绍与使用方法

    目录 功能介绍 视频导出操作 视频查看方法 本文已经首发在个人微信公众号:工业机器人仿真与编程(微信号:IndRobSim),欢迎关注! 功能介绍 DELIMA软件自身具备仿真动画视频导出功能,支持导 ...

  6. 简单介绍订单号或者流水号的生成方法

    一般订单号或者流水号等可能在一些平台会用到,然后我就简单的介绍一个我自己生成订单号和流水号的一个方法吧,如果程序有问题或者你有更好的生成办法,欢迎留言,留下你的文章链接,我们一起学习和进步哈. 方法简 ...

  7. 分布式ID | 这六种分布式ID生成方法,总有一款适合你

    Hi!我是小小,我们又见面了,我们今天的话题是六种分布式ID生成算法. 分布式ID简介 什么是分布式ID 在数据量不大的时候,单库单表完全可以支撑现有业务,数据量再大一点搞个MySql主从同步也可以. ...

  8. 分布式系统中 Unique ID 的生成方法

    一, 问题描述 在分布式系统存在多个 Shard 的场景中, 同时在各个 Shard 插入数据时, 怎么给这些数据生成全局的 unique ID? 在单机系统中 (例如一个 MySQL 实例), un ...

  9. UUID介绍与使用范围

    最近再做存储Multipath 以及/etc/fstab挂载得时候遇到需要查询磁盘得uuid得情况,查找到如下得信息,可以提供一起学习,交流 前言介绍 描述:通用唯一识别码(英语:Universall ...

最新文章

  1. AIFramework基本概念整理
  2. EventBus源码解析
  3. java jtable应用源码_JTable的应用(一)
  4. 变频器服务器电路板维修,变频器线路板常见维修方法
  5. Android搭建web,Android手机搭建WEB环境
  6. 高并发推荐系统架构设计实践
  7. 音标与字母发音不同的字母总结
  8. 如何实现文件的双向自动同步备份?
  9. Epub 转 txt
  10. 用R语言进行分位数回归
  11. 2021年5月19日最新快手半自动刷金币
  12. 大数据破获网售假耐克案
  13. 手把手系列之四十七—手把手教你做奶白鲫鱼汤
  14. 计算机制作效果图常用软件有,计算机园林效果图有哪些绘制过程?
  15. 塔望 · ​食界​人物|红牛饮料背后的两个企业家
  16. java使用Ip连接Oracle失败
  17. MapBoxSDK导入高版本unity2020、2021报错ARBackgroundRenderer
  18. ReID:常用损失函数总结
  19. 使用Matplotlib可视化数据的5个强大技巧
  20. message sent to deallocated instance 0x154eec600

热门文章

  1. OV7670使用经验总结
  2. websocket(二):SSM+websocket的聊天室
  3. 阿里云服务器买了,如何建站呀?
  4. Java知识点总结(JDBC-封装JDBC)
  5. 【mybatis】mybatis中 的# 和 $的区别
  6. 解决chrome浏览器us-yahoo.com搜索劫持
  7. win10内建子系统Linux
  8. .net中close和dispose及关闭流操作
  9. 1、Cocos2dx 3.0游戏开发三找一小块前言
  10. mysql基础 事务的认识和使用