web-dev-qa-db-ja.com

UUIDの短縮/再ハッシュ

まず第一に、再ハッシュは賢明なトピックであるという事実を認識していることを保証したいと思います。しかし、私はあなたの意見のいくつかを聞きたいです、あなたがここでどのようなアプローチを取るでしょう。

ノードがUUIDで識別されるエンティティをリモートで作成する分散アプリケーションを構築しています。最終的に、すべてのエンティティは、これらのUUIDを使用してすべてのエンティティを格納する専用のドレインノードに収集する必要があります。

次に、人間のユーザーにとってより便利な追加の識別子を作成したいと思います。 UUIDをBase64でエンコードすると、22文字のIDが作成されますが、これは人間による使用には適していません。だから私はURL短縮サービスのようなものが必要です。全単射関数を適用しても、情報の価値が低下しないため、役に立ちません。もちろん、IDを短くするために情報を失う必要があることは承知しています。また、ハッシュの情報を減らすと、衝突の可能性が高くなることも認識しています。私は立ち往生しています、人間のためのより短いIDを作成するために情報を減らすための最も適切な方法は何ですか。

いくつかの前提条件は次のとおりです。データストレージを介して{UUID、短縮ID}をマップする機能を提供します。私はまだ非集中型のソリューションを好みます。おそらく、合計で約100万のID(〜2 ^ 20)以上が必要になることはないでしょう。

これが私がこれまでに思いついた考えです:

  • 自動インクリメントID: ある種の自動インクリメントIDを使用する場合は、このIDを難読化された文字列に転送して渡すことができます。これが最も簡単なアプローチであり、周りにキーがほとんどない限り、キーはそれほど長くはありません。しかし、私は本当に欲しくない一元化されたエンティティを導入する必要があります。
  • UUIDを短くします。 元の128ビットuuidのビットの一部を取得することができました。次に、少なくともUUIDのバージョンを考慮に入れる必要があります。それともこれに何か問題がありますか?
  • UUIDの再ハッシュ: 最初のUUIDに2番目のハッシュアルゴリズムを適用して、マッピングを保存できます。

他にアプローチはありますか?何が有利ですか?

前もって感謝します!

27
b_erb

1)UUIDを短縮するには、単純にXOR上半分と下半分を組み合わせます(十分に短くなるまで繰り返します)。これにより、分布特性が保持されます。短縮する他のソリューションと同様に、出力、それは誕生日のパラドックスによる衝突の可能性を高めます

2)XORは些細なハッシュになりますが、追加のミキシングは必要ないので問題ありません。UUIDでCRCまたは非暗号化ハッシュを使用できますが、それが改善されるとは思いません。 。

3)あなたが受け入れる気があるならsome中央管理、それは苦痛である必要はありません。中央機関は、中規模のアドレス空間のブロックを各クライアントに割り当てることができ、クライアントはIDを割り当てるときにそのサブ範囲を反復処理できます。これにより、衝突が発生しないことが保証されますが、IDごとのラウンドトリップも回避されます。これを行う1つの方法は、IDに32ビット整数を使用し、一度に16ビットブロックを実行することです。つまり、最初のクライアントには0001が渡され、00010000から0001FFFFが許可されます。

4)UUIDを使用してデータベースに挿入できますが、IDフィールドもあります。これにより、代替のよりコンパクトな一意のIDが提供され、32ビット整数に制限できます。

23
Steven Sudit

人間にわかりやすい用語の辞書を選択し、それらを使用してUUID(の一部)を読みやすくする外部エイリアシングアプローチの使用を検討しましたか?

de305d54-75b4-431b-adb2-eb6b9e546013

65536語の辞書を使用すると、次のようになります。

de305d54-zebra-stackoverflow-extraneous-eb6b9e546013

ユーザーがこれらの人間が読める名前とメンタルハッシュの衝突(ゼブラが2回発生する)を目にする可能性は低く、データベースのサイズは大きくなりません。翻訳は全単射で純粋にUIです。

8

頭に浮かぶいくつかのこと:

あなたのユースケースは何ですか?分散方式でIDを生成することが懸念される場合、1つの解決策は、各マシンに独自のint IDを割り当て、それをIDのプレフィックスまたはサフィックスとして使用することです。

中央のエンティティがないために、ローカルでもIDを追跡する意味がない場合、これは実際には役に立ちません。 UUID自体からページを借用し、上記のように割り当てられたマシンIDと組み合わせてシステム時刻を使用できます。これにより、64ビット+マシンIDのサイズになります。基本的に、これはUUID V1スキームですが、マシンIDにMACアドレスよりも短いものを使用している点が異なります。 2010年2月12日以上の日付から開始できることがわかっている場合は、さらに短縮できる可能性があります。

ウィキペディアのUUIDエントリをまだ確認していない場合は、そこから独自の作成方法に関するアイデアを1つか2つ得ることができます。

3
Jim L

これが私が書いた簡単なハッシュアルゴリズムです。これを使用できます...読みやすさと衝突の可能性をトレードオフするために、入力と出力のマッピング、およびハッシュの長さを簡単に変更できます。

このアルゴリズムは、安全または効率的に設計されていませんが、うまくいくはずです。

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];
  }
}
1
Neromancer