【问题标题】:Generate short, unique identifiers [closed]生成简短的唯一标识符[关闭]
【发布时间】:2016-07-23 09:21:08
【问题描述】:

我正在寻找一种算法,它可以生成适合两者的标识符,例如外部使用。 URL 以及具有以下要求的持久性:

  • ,如最大值。 8 个字符
  • URL 友好,因此没有特殊字符
  • 人性化,例如没有像 L/l、0/O 这样的模棱两可的字符
  • 增量用于快速索引
  • 随机以防止在不知道算法的情况下猜测(会很好,但不重要)
  • 独特,无需检查数据库

我查看了各种解决方案,但我发现所有解决方案都有一些重大的权衡。例如:

  • GUID:太长,不是增量的
  • GUID base64 编码:仍然太长,不是增量
  • GUID ascii85 编码:短,不是增量,太多不合适的字符
  • base32、base36 等 GUID 编码:短,但会丢失信息
  • 梳子 GUID:太长,但增量
  • 所有其他基于随机:需要检查数据库的唯一性
  • 基于时间:在集群或多线程环境中容易发生冲突

编辑:为什么这被标记为离题?这些要求描述了一个可以提供许多合法解决方案的特定问题。事实上,这里的一些解决方案非常好,我正在努力选择一个标记为答案。

【问题讨论】:

  • 根据定义它们不能既是随机的又是增量的!!
  • @MitchWheat 是的,这就是为什么我提到随机性并不那么重要
  • 另外:“所有其他基于随机:需要检查数据库的唯一性”:如果它很长,那么您可以减少冲突(想想 GUID);如果它很短,那么如果您需要唯一性,则必须检查数据库。您有太多相互冲突的要求。
  • 简短、独特、随机 - 选择任意两个。
  • 同意@MitchWheat,你有太多相互冲突的要求。考虑到你有所有的要求,你有什么理由不能自己实现吗?您需要解决方案或建议吗?

标签: c# .net guid uniqueidentifier identifier


【解决方案1】:

以下使用已知唯一的 ID(因为它来自关系数据库中的唯一 ID 列)和字母和数字的随机序列的组合来生成令牌:

public static string GenerateAccessToken(string uniqueId) // generates a unique, random, and alphanumeric token
{
    const string availableChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    using (var generator = new RNGCryptoServiceProvider())
    {
        var bytes = new byte[16];
        generator.GetBytes(bytes);
        var chars = bytes.Select(b => availableChars[b % availableChars.Length]);
        var token = new string(chars.ToArray());
        return uniqueId + token;
    }
}

保证令牌既是唯一的又是随机的(或至少是“伪随机”)。你可以通过改变bytes的长度来控制长度。

为避免混淆“0”和“O”或“l”和“1”,您可以从availableChars 中删除这些字符。


编辑

我刚刚意识到这并没有完全通过“无数据库检查”的要求,但是当我使用这样的代码时,我一直在内存中已经有了一个我知道包含唯一 ID 的实体,所以我我希望这同样适用于你的情况。我认为不可能完全满足您的所有要求,所以我希望这仍然是属性的良好平衡。

【讨论】:

    【解决方案2】:

    如果可能的话,我会将用户需求(简短、易读)和数据库需求(增量、快速索引)分开。面向用户的需求发生变化。您不想修改表格,因为明天您决定更改面向用户的 ID 的长度或其他细节。

    一种方法是使用用户友好的字符(例如
    23456789ABCDEFGHJKLMNPQRSTUVWXYZ)生成您的 ID,并使其随机化。

    但是在插入数据库时​​,不要将该值作为它引用的记录的主键,甚至不要将其存储在该表中。使用标识主键将其插入到自己的表中,然后将 intbigint 键与您的记录一起存储。

    这样你的主表就可以有一个增量主键。如果您需要通过其“友好”ID 引用记录,则加入您的友好 ID 表。

    我的猜测是,如果您生成的这些 ID 数量足够多,以至于您担心索引性能,那么人类用户检索这些值的速率会低得多。因此,在友好 ID 表中查找随机值稍慢一点不会有问题。

    【讨论】:

    • 接受,因为这实际上不是一个坏主意。有两个标识符似乎是多余的,但你是对的,本质上这些要求反映了两个不同的问题。 :)
    【解决方案3】:

    您是否尝试过 proquints

    Proquint 是一个 PRO 名词 QUINT-uplet,由交替的明确辅音和元音组成,例如:“lusab”。

    我认为它们几乎满足您的所有要求。

    请参阅提案here。 而here是C和Java的官方实现。

    我开发了一个 .NET 端口,您可以将其下载为 Proquint.NET

    【讨论】:

    • 我喜欢这样。 ID 本身不是用户友好的,但它可以转换为用户友好的表示形式。
    • 您是否必须担心在某些时候它会产生一些听起来令人反感的东西?
    • ["bosom","bum","fuk","gonad","guro","hobag","homo","kafir","kum","mofo"," moron","nazi","pubis","sodom","tit","titi","vagin","vomit"] 这些是结果中可能出现的几个词。这几个词来自~1700 bad words here,按长度
    【解决方案4】:

    我之前实施的一个简单解决方案无法满足您的所有限制条件,但如果您对问题的看法稍有不同,则可能是可以接受的。

    首先,我使用了一个函数来混淆数据库 ID func(id) => yfunc(y) => id。 (我使用了Feistel cipherhere 是实现此类功能的一个示例)其次,将经过混淆的 id 转换为 base 62,使其变得简短且对 url 友好。 (您可以使用较小的字符集来实现人性化)这将创建从数据库 id 到字符串标识符的一对一映射。在我的实现中,1、2 对应地映射到 2PawdM、5eeGE8,我可以从字符串 2PawdM 和 5eeGE8 中获取数据库 id 1、2。当您使用不同的混淆函数时,映射可能会完全不同。

    使用此解决方案,标识符本身不是增量的,但是,由于标识符直接映射到数据库 id,因此您可以计算相应的数据库 id 并直接对 id 列进行任何数据库查询。您不需要生成字符串标识符并将其存储到数据库中,当您使用自增的 id 列存储记录时,数据库本身会保证唯一性。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-08-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-20
      • 1970-01-01
      • 1970-01-01
      • 2015-05-06
      相关资源
      最近更新 更多