目录
UUID定义
UUID是国际标准化组织(ISO)提出的一个概念。UUID是一个128比特的数值,这个数值可以通过一定的算法计算出来。为了提高效率,常用的UUID可缩短至16位。UUID用来识别属性类型,在所有空间和时间上被视为唯一的标识。一般来说,可以保证这个值是真正唯一的任何地方产生的任意一个UUID都不会有相同的值。使用UUID的一个好处是可以为新的服务创建新的标识符。这样一来,客户端在查找一个服务时,只需要在它的服务查找请求中指出与某类服务(或某个特定服务)有关的UUID,如果服务的提供者能将可用的服务与这个UUID相匹配,就返回一个响应。
UUID设计目的
UUID的设计目的简单来说,就是让分布式系统中的所有元素,都能有一个唯一ID。UUID我们一般做开发的都会用到。生成一个UUID也很简单。
UUID的组成
三部分组成。
当前日期和时间,UUID的第一个部分与时间有关,如果你在生成一个UUID之后,过几秒又生成一个UUID,则第一个部分不同,其余相同。
时钟序列。
全局唯一的IEEE机器识别号,如果有网卡,从网卡MAC地址获得,没有网卡以其他方式获得
时间戳(当前日期+时间)+ 时钟序列 + 机器识别号(MAC、其他)
UUID的版本
uuid有五个版本。分别是:
version1: 基于时间的
基于时间的UUID通过计算当前时间戳、随机数和机器MAC地址得到。由于在算法中使用了MAC地址,这个版本的UUID可以保证在全球范围的唯一性。但与此同时,使用MAC地址会带来安全性问题,这就是这个版本UUID受到批评的地方。如果应用只是在局域网中使用,也可以使用退化的算法,以IP地址来代替MAC地址--Java的UUID往往是这样实现的(当然也考虑了获取MAC的难度)。
version2: 基于DCE的
DCE(Distributed Computing Environment)安全的UUID和基于时间的UUID算法相同,但会把时间戳的前4位置换为POSIX的UID或GID。这个版本的UUID在实际中较少用到
version3: 基于MD5的
基于名字的UUID通过计算名字和名字空间的MD5散列值得到。这个版本的UUID保证了:相同名字空间中不同名字生成的UUID的唯一性;不同名字空间中的UUID的唯一性;相同名字空间中相同名字的UUID重复生成是相同的
version4: 基于伪随机数的
根据随机数,或者伪随机数生成UUID。这种UUID产生重复的概率是可以计算出来的,但随机的东西就像是买彩票:你指望它发财是不可能的
version5: 基于SHA1的
和基于名字的UUID算法类似,只是散列值计算使用SHA1(Secure Hash Algorithm 1)算法
UUID的样例
@Test
public void uuid(){
String uuid = UUID.randomUUID().toString();
System.out.println(uuid);
}
3b50fd3e-387e-49e6-844a-2dc407ed2223
be1540de-1265-423a-b5e4-c140da294899
查看源码:
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);
}
我们可以发现,java.util自带的uuid是采用的版本4。也就基于伪随机数的算法版本。
UUID是否可以做订单ID?
uuid在不考虑时钟回拨问题的基础上,其实是可以做订单ID的,但是绝对不能使用java.util的基于伪随机数的方式。而是建议采用版本1,基于时间的。任何基于随机数的方法都不能确保不重复问题
UUID为什么不建议做订单ID?
但是我们为什么一般不希望采用UUID做订单呢?
首先-很难区分先后时间:
uuid很难通过肉眼判断订单的先后关系,必须去看创建时间。
其次-表空间占用更大:
数据库类型中,我们看一下下表,数据库的类型大小
字段类型 | 大小 |
tinyint | 1字节 |
int | 4字节 |
bigint | 8字节 |
char | 0-255字节 |
varchar | 0-65535字节 |
如果使用uuid的方式,一般UUID生成以后都是一个36长度字串组成,我们必须采用varchar类型存储,非常占用空间
最后-对于mysql的B+Tree效率更低:
数据入库操作:因为数据库都有事务性要求,存储引擎是InnoDB。该引擎存储数据的时候采用的是B+Tree存储。B+Tree的数据区是天然有序的。UUID的数据大小不可控的,根据B+Tree的要求,为了保持数据有序,数据等于插入操作。而如果订单ID是有序的,数据只需要追加操作就好了。
数据库查询操作: mysql查询数据库的时候,是需要先将数据从磁盘加载到内存中。这是一个标准的I/O动作。InnoDb每次加载数据的基本单位是16KB。如果UUID占了36byte,而int是4byte。那么每次加载数据分别是:16KB/36byte和16KB/4byte。可以看到,UUID因为占用空间更大,IO的读取次数肯定会相应增加
那么什么方法更适合生存订单ID呢?
雪花算法、美团的leaf算法等