首先,我想确保我知道这一事实,重新发布是一个明智的话题。不过,我想听听你的一些意见,你会采取什么方法。
我正在构建一个分布式应用程序,其中节点远程创建由UUID标识的实体。最终,应该在专用的漏极节点处收集所有实体,该节点通过使用这些UUID来存储所有实体。
现在我想创建额外的标识符,这对人类用户来说更方便。编码UUID的Base64仍会创建22个字符的ID,这不适合人类使用。所以我需要像URL缩短服务这样的东西。应用双射函数无济于事,因为它们不会降低信息价值。当然,我知道我需要丢失信息才能缩短身份。而且我也知道任何哈希信息的减少都会增加冲突的可能性。 我被卡住了,为了给人类创造更短的ID,减少信息的最合适的方法是什么。
以下是一些先决条件:我将提供通过我的数据存储映射{UUID,缩短ID}的功能。我仍然更喜欢非集中式解决方案。我可能永远不会需要超过大约一百万的ID(~2 ^ 20)。
到目前为止,我提出了以下想法:
还有其他方法吗?什么是有利的?
提前致谢!
答案 0 :(得分:23)
1)为了缩短UUID,您可以简单地将上半部分与底部进行异或(并重复直到它足够短)。这将保留分布特征。像任何缩短输出的解决方案一样,它会增加因生日悖论而发生碰撞的可能性
2)XOR相当于一个简单的哈希,但由于不需要额外的混合,所以没关系。您可以在UUID上使用CRC或非加密哈希,但我不相信它有任何改进。
3)如果您愿意接受某些集中管理,那就不一定非常痛苦。中央权限可以向每个客户端发出中等大小的地址空间块,然后客户端可以在分配ID时遍历该子范围。这可以保证不会发生冲突,但也可以避免每个ID的往返。一种方法是使用32位整数作为ID,一次输出一个16位的块。换句话说,第一个客户端获得了0001,这允许00010000到0001FFFF。
4)您可以使用UUID插入数据库,但也有一个标识字段。这将提供一个替代的,更紧凑的唯一ID,可以限制为32位int。
答案 1 :(得分:8)
您是否考虑过使用外部别名方法,在这种方法中,您选择了一个人性化术语词典,并使用它们使UUID(的一部分)更具可读性:
de305d54-75b4-431b-adb2-eb6b9e546013
使用65536个单词的字典可能会变成:
de305d54-zebra-stackoverflow-extraneous-eb6b9e546013
用户不太可能看到使用这些人类可读名称的精神哈希冲突(斑马发生两次),并且您的数据库的大小不会增加。翻译是双向的,纯粹是UI。
答案 2 :(得分:3)
只是想到几件事:
你的用例是什么?如果您担心的是您将以分布式方式生成ID,则一种解决方案是为每台计算机分配它自己唯一的int id,并将其用作其ID的前缀或后缀。
如果没有一个中心实体,那么即使在本地也无法跟踪ID,这并没有多大帮助。您可以从UUID本身借用一个页面,并将系统时间与上面指定的机器ID一起使用。这可以让你降低到64位+你的机器ID大小。基本上,这是UUID V1方案,除了您使用比MAC地址短的机器ID。如果你知道你可以从日期> = 2010年2月12日开始,你可能会进一步缩短。
查看维基百科UUID条目(如果您还没有),您可以从那里了解如何构建自己的条目。
答案 3 :(得分:1)
这是我写的一个简单的哈希算法。您可以使用此...您可以轻松更改输入和输出映射以及散列的长度,以便将可读性与冲突可能性进行权衡。
此算法并非设计为安全或有效,但应该可以解决问题。
public class HashTools {
final static String inputMapping = "0123456789ABCDEF";
final static String[] outputMapping = new String[] {
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H",
"I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"
};
/* Input: String - containing mostly letters / numbers
* Output: <hashLength> String using 0-9,A-Z encoding
*/
public static String simpleHash(String str, int hashLength) {
StringBuilder hashStr = new StringBuilder(hashLength);
String strUpper = str.toUpperCase();
int[] hash = new int[hashLength];
int i, j, num;
for (i = 0; i < strUpper.length(); i++) {
char strChar = strUpper.charAt(i);
num = mapCharToInt(strChar);
j = i % hashLength;
hash[j] += num;
}
for (i = 0; i < hashLength; i++) {
hashStr.append(mapIntToHashChar(hash[i]));
}
return hashStr.toString();
}
private static int mapCharToInt(char hexChar) {
return inputMapping.indexOf(hexChar);
}
private static String mapIntToHashChar(int num) {
return outputMapping[num % outputMapping.length];
}
}