转载:(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
-
This specification defines a Uniform Resource Name namespace for
-
UUIDs (Universally Unique IDentifier), also known as GUIDs (Globally
-
Unique IDentifier). A UUID is 128 bits long, and can guarantee
-
uniqueness across space and time. UUIDs were originally used in the
-
Apollo Network Computing System and later in the Open Software
-
Foundation's (OSF) Distributed Computing Environment (DCE), and then
-
in Microsoft Windows platforms.
-
This specification is derived from the DCE specification with the
-
kind permission of the OSF (now known as The Open Group).
-
Information from earlier versions of the DCE specification have been
-
incorporated into this document.
2 版本
UUID本身也经过了多个版本的演化,每个版本的算法都不同。
2.1 Version 1
基于时间的UUID。通过计算当前时间戳、随机数和机器MAC地址得到,
由于在算法中使用了MAC地址,这个版本的UUID可以保证在全球范围的唯一性。
但与此同时,因为它暴露了电脑的MAC地址和生成这个UUID的时间,这就是这个版本UUID被诟病的地方,使用MAC地址会带来安全性问题。
如果应用只是在局域网中使用,也可以使用退化的算法,以IP地址来代替MAC地址。
在python里面的使用的例子:
-
>>> import uuid
-
>>> uuid.uuid1()
-
UUID('444b5cc0-ae5d-11e6-8d22-28924a431726')
-
>>> uuid.uuid1()
-
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保证了:
- 相同名字空间中不同名字生成的UUID的唯一性;
- 不同名字空间中的UUID的唯一性;
- 相同名字空间中相同名字的UUID重复生成是相同的。
由用户指定1个namespace和1个具体的字符串,通过MD5散列,来生成1个UUID;
根据规范描述,这个版本的存在是为了向后兼容?平时这个版本我们也很少用到
在python里面的使用的例子:
-
>>> import uuid
-
>>> uuid.uuid3(uuid.NAMESPACE_DNS, "myString")
-
UUID('21fc48e5-63f0-3849-8b9d-838a012a5936')
-
>>> uuid.uuid3(uuid.NAMESPACE_DNS, "myString")
-
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里面使用的例子:
-
>>> import uuid
-
>>> uuid.uuid4()
-
UUID('e584539d-a334-4f15-9819-88d73fcf707d')
-
>>> uuid.uuid4()
-
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里面的使用的例子:
-
>>> import uuid
-
>>> uuid.uuid5(uuid.NAMESPACE_DNS, "myString")
-
UUID('cd086011-6aac-5a06-a94a-0b67c59649ba')
-
>>> uuid.uuid5(uuid.NAMESPACE_DNS, "myString")
-
UUID('cd086011-6aac-5a06-a94a-0b67c59649ba')
3 应用
Version 1/2适合应用于分布式计算环境下,具有高度的唯一性。
Version 3/5适合于一定范围内名字唯一,且需要或可能会重复生成UUID的环境下。
Version4在数据量亿级以下或者唯一性要求不严谨的情况下使用。
Java中UUID提供了两个方法:randomUUID()和nameUUIDFromBytes(byte[] name),分别对应Version4和Version3。jdk实现如下
-
public static UUID randomUUID() {
-
SecureRandom ng = Holder.numberGenerator;
-
byte[] randomBytes = new byte[16];
-
ng.nextBytes(randomBytes);
-
randomBytes[6] &= 0x0f; /* clear version */
-
randomBytes[6] |= 0x40; /* set to version 4 */
-
randomBytes[8] &= 0x3f; /* clear variant */
-
randomBytes[8] |= 0x80; /* set to IETF variant */
-
return new UUID(randomBytes);
-
}
-
public static UUID nameUUIDFromBytes(byte[] name) {
-
MessageDigest md;
-
try {
-
md = MessageDigest.getInstance("MD5");
-
} catch (NoSuchAlgorithmException nsae) {
-
throw new InternalError("MD5 not supported", nsae);
-
}
-
byte[] md5Bytes = md.digest(name);
-
md5Bytes[6] &= 0x0f; /* clear version */
-
md5Bytes[6] |= 0x30; /* set to version 3 */
-
md5Bytes[8] &= 0x3f; /* clear variant */
-
md5Bytes[8] |= 0x80; /* set to IETF variant */
-
return new UUID(md5Bytes);
-
}
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://msdn.microsoft.com/en-us/library/system.guid(v=vs.110).aspx
- Linux:http://en.wikipedia.org/wiki/Util-linux
- Android:http://developer.android.com/reference/java/util/UUID.html
- PHP:http://php.net/manual/en/function.uniqid.php#94959
- MySQL:http://dev.mysql.com/doc/refman/5.1/en/miscellaneous-functions.html#function_uuid
- Java:http://docs.oracle.com/javase/7/docs/api/java/util/UUID.html
- nodejs - https://github.com/broofa/node-uuid
参考:
http://monkeybean.cn/2018/07/06/uuid_introduce/
关于UUID的二三事 - 简书