分布式UUID的生成
1.通过jdk标准api?uuid会重复
要生成uuid,大多会直接使用下面这句:
uuid.randomuuid().tostring().replace("-", "");
在多数情况,这样的处理是没问题的,毕竟是jdk标准接口。但是在某些情况下,会出现重复。搜素 uuid 重复,就会发现有人踩到了雷
先看uuid各版本的实现原理:universally unique identifier
再看jdk的实现(只实现了uuid的1,3,4版本):java.util.uuid
会发现在分布式场景下jdk自带的这个工具类并不好用。原因:
- 会存在多台web容器在同1个物理/云主机上,mac地址相同。因此,版本1的uuid,不合适
- randomuuid实现的是uuid的版本4,产生重复的概率是可以计算出来的,海量存储时,重复不可避免。这也是有人踩雷的原因
- nameuuidfrombytes实现的是uuid的版本3,保证种子的唯一性才能确保生成的uuid唯一。在分布式的场景下,如果我们每次都能获取到唯一的种子,那也就不必用这个方法生成uuid了
2.数据库获取uuid?性能不能保证
通过这种消耗大量性能来获取uuid,当然可行,但在高并发的场景下你真的会去考虑吗?
3.分布式uuid的生成
分布式?多台web容器(我们可以称之为实例)在同1个机器(mac地址相同)下?不依赖第3方工具?最好在jvm解决?
思路
确保每台实例具有唯一的名字(我们可以称之为实例名)
确保某台实例生成的uuid不会重复: 当前系统时间 + 递增的数值(避免高并发的影响)
因此,算法如下:
uuid = 实例名 + 当前系统时间毫秒数 + 递增的int数
方法
-
对每台web容器的java_options配置不一样的实例名
以tomcat(8.0.53)为例,在startup.bat里配置:
rem to set java_opts set "java_opts=%java_opts% -dinstance.name=cico-mba"
这样,上文的instance.name,就变成了jvm里的1个参数了
-
代码实现
package java.main; import java.util.concurrent.atomic.atomicinteger; public class uuidutil { /* 从当前web容器的java_options中,获取jvm的配置:当前实例名 */ private static final string instance_name = system.getproperty("instance.name"); /* 实例名脱敏后的值 */ private static string instance_name_by_num = null; /* 计数器。atomicinteger是java.util.concurrent下的类,jdk的算法工程师会避免并发问题 */ private static atomicinteger cnt = new atomicinteger(0); /** * 初始化instance_name_by_num。需考虑并发 */ private synchronized static void initinstancenamebynum() { if (null != instance_name_by_num) { return; } char[] chars = instance_name.tochararray(); stringbuilder sb = new stringbuilder(); for (char c : chars) { sb.append((int) c); } instance_name_by_num = sb.tostring(); } /** * 生成分布式的uuid * * @return */ public static string getconcurrentuuid() { if (null == instance_name) { return "the jvm option is null, named 'instance.name'"; } if (null == instance_name_by_num) { initinstancenamebynum(); } stringbuilder uuid = new stringbuilder(); uuid.append(instance_name_by_num); uuid.append(system.currenttimemillis()); uuid.append(cnt.incrementandget()); return uuid.tostring(); } }
说明
通过上文的方法可在jvm内快速生成支持分布式的uuid。这个uuid的长度,由下面3部分组成:
- 13: system.currenttimemillis()的长度是13位
- 11: integer.min_value的长度。int值从0开始递增,达到int的上限后,会从负数开始重新计数,因此长度最大是11位
- 2 * 实例名的字符数: 实例名一般由字母、数字、小数点、减号、下划线组成,这些字符的ascii码值是2位
如果这个uuid需要持久化,持久化的字段可定义成varchar2(255),其中实例名的字符长度最大可以是115 = ( 255 - 13 - 11 ) / 2
上一篇: 关于canvas
下一篇: C++调用Python脚本中的函数