Bootstrap

UUID 详解

转载:(100条消息) UUID详解_zzhongcy的博客-CSDN博客

想必大家都用过UUID,但是UUID也有可能重复,在某些情况下我们需要注意。最近在网上看了几篇关于说明UUID的文章,这里转载记录一下。

实体类使用String id,列使用varchar(50),因为本身占36,偶尔还会有一些连接符,一般不会超过40

1 定义

UUID 是 通用唯一识别码(Universally Unique Identifier)的缩写,目的是让分布式系统中的所有元素,都有唯一辨识,而不需要通过中央控制端来做辨识指定。
由算法机器生成。为保证UUID的唯一性,规范定义了包括网卡MAC地址、时间戳、名字空间(Namespace)、随机或伪随机数、时序等元素,以及从这些元素生成UUID的算法。UUID的复杂特性在保证了其唯一性的同时,意味着只能由计算机生成。
非人工指定,非人工识别。UUID是不能人工指定的,除非你冒着UUID重复的风险。UUID的复杂性决定了“一般人“不能直接从一个UUID知道哪个对象和它关联。

1.1 规范定义

UUID来自于IETF发布的一个规范:A Universally Unique IDentifier (UUID) URN Namespace

UUID来源于OSF的DCE规范,也就是RFC4122的前身

GUID来源于微软,注意RFC4122的作者之一是微软员工

下面摘录一下,RFC4144中的Abstract

 
  1. This specification defines a Uniform Resource Name namespace for

  2. UUIDs (Universally Unique IDentifier), also known as GUIDs (Globally

  3. Unique IDentifier). A UUID is 128 bits long, and can guarantee

  4. uniqueness across space and time. UUIDs were originally used in the

  5. Apollo Network Computing System and later in the Open Software

  6. Foundation's (OSF) Distributed Computing Environment (DCE), and then

  7. in Microsoft Windows platforms.

  8. This specification is derived from the DCE specification with the

  9. kind permission of the OSF (now known as The Open Group).

  10. Information from earlier versions of the DCE specification have been

  11. incorporated into this document.

2 版本

UUID本身也经过了多个版本的演化,每个版本的算法都不同。

2.1 Version 1

基于时间的UUID。通过计算当前时间戳、随机数和机器MAC地址得到,

由于在算法中使用了MAC地址,这个版本的UUID可以保证在全球范围的唯一性。

但与此同时,因为它暴露了电脑的MAC地址和生成这个UUID的时间,这就是这个版本UUID被诟病的地方,使用MAC地址会带来安全性问题。

如果应用只是在局域网中使用,也可以使用退化的算法,以IP地址来代替MAC地址。

在python里面的使用的例子:

 
  1. >>> import uuid

  2. >>> uuid.uuid1()

  3. UUID('444b5cc0-ae5d-11e6-8d22-28924a431726')

  4. >>> uuid.uuid1()

  5. UUID('46a9bf21-ae5d-11e6-9549-28924a431726')

其中,最后的12个字符28924a431726就是我电脑网卡的MAC地址

2.2 Version 2

DCE(Distributed Computing Environment)安全的UUID。和基于时间的UUID算法相同,但会把时间戳的前4位置换为POSIX的UID或GID。

不过,在UUID的规范里面没有明确地指定,所以基本上所有的UUID实现都不会实现这个版本。

2.3 Version 3

基于名字的UUID(MD5)。通过计算名字和名字空间的MD5散列值得到。

这个版本的UUID保证了:

  1. 相同名字空间中不同名字生成的UUID的唯一性;
  2. 不同名字空间中的UUID的唯一性;
  3. 相同名字空间中相同名字的UUID重复生成是相同的。

由用户指定1个namespace和1个具体的字符串,通过MD5散列,来生成1个UUID;

根据规范描述,这个版本的存在是为了向后兼容?平时这个版本我们也很少用到

在python里面的使用的例子:

 
  1. >>> import uuid

  2. >>> uuid.uuid3(uuid.NAMESPACE_DNS, "myString")

  3. UUID('21fc48e5-63f0-3849-8b9d-838a012a5936')

  4. >>> uuid.uuid3(uuid.NAMESPACE_DNS, "myString")

  5. UUID('21fc48e5-63f0-3849-8b9d-838a012a5936')

在java中使用的例子

System.out.println(UUID.nameUUIDFromBytes("myString".getBytes("UTF-8")).toString());

Java只支持生成版本3和版本4的UUID

2.4 Version 4

随机UUID。根据随机数,或者伪随机数生成UUID。这种UUID产生有一定的重复概率但是极低。

根据随机数,或者伪随机数生成UUID。这种UUID产生重复的概率是可以计算出来的,但随机的东西就像是买彩票:你指望它发财是不可能的,但狗屎运通常会在不经意中到来。这个版本应该是平时大家无意中用得最多的版本了;

在python里面使用的例子:

 
  1. >>> import uuid

  2. >>> uuid.uuid4()

  3. UUID('e584539d-a334-4f15-9819-88d73fcf707d')

  4. >>> uuid.uuid4()

  5. UUID('76ec02cc-1b1d-4ad3-bd09-a4f6d67c7af4')

以及Java中大家最熟悉的:

System.out.println(UUID.randomUUID().toString());

2.5 Version 5

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

和版本3一样,不过散列函数换成了SHA1

在python里面的使用的例子:

 
  1. >>> import uuid

  2. >>> uuid.uuid5(uuid.NAMESPACE_DNS, "myString")

  3. UUID('cd086011-6aac-5a06-a94a-0b67c59649ba')

  4. >>> uuid.uuid5(uuid.NAMESPACE_DNS, "myString")

  5. UUID('cd086011-6aac-5a06-a94a-0b67c59649ba')

3 应用

Version 1/2适合应用于分布式计算环境下,具有高度的唯一性。
Version 3/5适合于一定范围内名字唯一,且需要或可能会重复生成UUID的环境下。
Version4在数据量亿级以下或者唯一性要求不严谨的情况下使用。


Java中UUID提供了两个方法:randomUUID()和nameUUIDFromBytes(byte[] name),分别对应Version4和Version3。jdk实现如下

 
  1. public static UUID randomUUID() {

  2. SecureRandom ng = Holder.numberGenerator;

  3. byte[] randomBytes = new byte[16];

  4. ng.nextBytes(randomBytes);

  5. randomBytes[6] &= 0x0f; /* clear version */

  6. randomBytes[6] |= 0x40; /* set to version 4 */

  7. randomBytes[8] &= 0x3f; /* clear variant */

  8. randomBytes[8] |= 0x80; /* set to IETF variant */

  9. return new UUID(randomBytes);

  10. }

 
  1. public static UUID nameUUIDFromBytes(byte[] name) {

  2. MessageDigest md;

  3. try {

  4. md = MessageDigest.getInstance("MD5");

  5. } catch (NoSuchAlgorithmException nsae) {

  6. throw new InternalError("MD5 not supported", nsae);

  7. }

  8. byte[] md5Bytes = md.digest(name);

  9. md5Bytes[6] &= 0x0f; /* clear version */

  10. md5Bytes[6] |= 0x30; /* set to version 3 */

  11. md5Bytes[8] &= 0x3f; /* clear variant */

  12. md5Bytes[8] |= 0x80; /* set to IETF variant */

  13. return new UUID(md5Bytes);

  14. }

4. 扩展

减小Version4重复概率

System.currentTimeMillis() + “_” + UUID.randomUUID().toString()生成结果示例:

1530454044000_bec863fc-92a4-4c62-8156-7ef9c0169abc

可大幅度减少Version4的重复概率,缺点是拼接字符串后的值太长,长度为13(时间戳长度) + 1(连接字符) + 36(32位实际UUID加4位连接字符) = 50。

5、UUID的应用

从几个版本的定义来看,感觉都不是特别完美,可能版本4是平时用得最多的,但是在现实的业务场景中,考虑到可读性、唯一性、长度,我们一般也不会选择UUID当做数据库的主键。

至于其他场景的应用,可以结合具体的场景,来使用各个版本的实现。

6、UUID和各个编程语言

参考:

http://monkeybean.cn/2018/07/06/uuid_introduce/
关于UUID的二三事 - 简书

;