我目前正在使用Hibernate作为JPA提供程序,我想将ID生成从持久层切换到应用程序层。
我的架构(在MySQL 5.7上)目前正在使用BIGINT(20)
数据类型来识别ID,但我不想重构以获取UUID。
所以我认为像是"系统" UID应该足够了:
public static long getUID()
{
long key = getSystemKey() << 56; // like 0x0100000000000000L;
long ts = System.currentTimeMillis() << 12;
int r = new Random().nextInt(0xFFF);
long id = key + ts + r;
return id;
}
生成的ID的格式为
KK TTTTTTTTTTT RRR
其中getSystemKey()
[K
]返回每个&#34;机器的唯一固定字节&#34;应用程序正在运行(它在配置文件中声明)。
时间戳ts
[T
]正在使用11个nybbles,确保有足够的毫秒到2527-06-23 08:20:44.415
random r
[R
]用于添加每台机器每毫秒的随机性(最后3个nybbles)。
所以我想知道这种方式是否足够一致,有什么利弊,以及是否有更好的方式。
由于
我用100个线程和10,000个执行测试了这个方法:
public static void main(String[] args) throws Exception
{
List<Callable<Long>> runners = new ArrayList<>();
for(int i = 0; i < 10000; i++)
{
runners.add(SUID::random);
}
ExecutorService pool = Executors.newFixedThreadPool(100);
List<Future<Long>> results = pool.invokeAll(runners);
pool.shutdown();
int dups = 0;
Set<Long> ids = new HashSet<>();
for(Future<Long> future : results)
{
if(!ids.add(future.get()))
{
dups++;
}
}
System.out.println(dups);
}
我有大约6%的碰撞。
所以,唯一的方法似乎是使用一些同步:
public final class SUID
{
private static final AtomicLong SEQUENCE = new AtomicLong(Config.getSystemKey() << 56 | System.currentTimeMillis() << 12);
private SUID()
{
super();
}
public static long generate()
{
return SEQUENCE.incrementAndGet();
}
}