缩短/重组UUID

时间:2010-02-12 17:19:54

标签: hash url-shortener

首先,我想确保我知道这一事实,重新发布是一个明智的话题。不过,我想听听你的一些意见,你会采取什么方法。

我正在构建一个分布式应用程序,其中节点远程创建由UUID标识的实体。最终,应该在专用的漏极节点处收集所有实体,该节点通过使用这些UUID来存储所有实体。

现在我想创建额外的标识符,这对人类用户来说更方便。编码UUID的Base64仍会创建22个字符的ID,这不适合人类使用。所以我需要像URL缩短服务这样的东西。应用双射函数无济于事,因为它们不会降低信息价值。当然,我知道我需要丢失信息才能缩短身份。而且我也知道任何哈希信息的减少都会增加冲突的可能性。 我被卡住了,为了给人类创造更短的ID,减少信息的最合适的方法是什么。

以下是一些先决条件:我将提供通过我的数据存储映射{UUID,缩短ID}的功能。我仍然更喜欢非集中式解决方案。我可能永远不会需要超过大约一百万的ID(~2 ^ 20)。

到目前为止,我提出了以下想法:

  • 自动递增ID: 如果我使用某种自动递增的ID,我可以将此id转移到混淆的字符串并传递它。这将是最简单的方法,只要周围的键很少,键就不会很长。但是我必须引入一个我不想要的集中实体。
  • 缩短UUID:我可以只取一些原始的128位uuid。然后我应该至少考虑UUID的版本。或者这有什么问题吗?
  • 重新发布UUID:我可以在初始UUID上应用第二个哈希算法并存储映射。

还有其他方法吗?什么是有利的?

提前致谢!

4 个答案:

答案 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];
  }
}