生成人类可读/可用的,简短但唯一的ID


86
  • 每天需要处理> 1000个但<10000个新记录

  • 无法使用GUID / UUID,自动递增编号等。

  • 理想情况下,长度应为5或6个字符,当然可以为alpha

  • 希望重用现有的知名算法(如果有)

那里有东西吗?


为什么不使用自动递增的INT或BIGINT?它可能是最易读的,并且可以轻松处理该卷。
2012年

根据上述Q值,尝试将其保持在最大5/6个字符并每天最多支持9999个新记录
Kumar

@Kumar-如果一天中需要超过9999条记录怎么办?您提出的解决方案听起来站不住脚。
ChaosPandion 2012年

@ChaosPandion:我认为这些大概是负载/流量的粗略猜测,而不是硬边界。我不确定您为什么要对每日交易次数设置任意上限。
保罗·萨西克

您可以将其编码为base 64并使用它。我不确定您可以将其缩小到小于该值,并且仍然使用可读字符。但是我认为base 64的可读性远低于base 32,因为它要求对大多数字符(大写f,低o,低o与仅f,oo)添加一个额外的限定符。
Malk 2012年

Answers:


118

tinyurl和bit.ly将Base 62用作缩写URL。这是一种易于理解的方法,用于创建“唯一的”人类可读的ID。当然,您将必须存储创建的ID,并在创建时检查重复项以确保唯一性。(请参见答案底部的代码)

基于62个唯一性指标

以62为基数的5个字符将为您提供62 ^ 5个唯一ID = 916,132,832(〜10亿)如果每天使用1万个ID,您可以使用91k +天

以62为基数的6个字符将为您提供62 ^ 6个唯一ID = 56,800,235,584(56+十亿)每天以1万个ID可以运行5+百万天

基于36个唯一性指标

6个字符将为您提供36 ^ 6个唯一ID = 2,176,782,336(2+十亿)

7个字符将为您提供36 ^ 7个唯一ID = 78,364,164,096(78+十亿)

码:

public void TestRandomIdGenerator()
{
    // create five IDs of six, base 62 characters
    for (int i=0; i<5; i++) Console.WriteLine(RandomIdGenerator.GetBase62(6));

    // create five IDs of eight base 36 characters
    for (int i=0; i<5; i++) Console.WriteLine(RandomIdGenerator.GetBase36(8));
}

public static class RandomIdGenerator 
{
    private static char[] _base62chars = 
        "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
        .ToCharArray();

    private static Random _random = new Random();

    public static string GetBase62(int length) 
    {
        var sb = new StringBuilder(length);

        for (int i=0; i<length; i++) 
            sb.Append(_base62chars[_random.Next(62)]);

        return sb.ToString();
    }       

    public static string GetBase36(int length) 
    {
        var sb = new StringBuilder(length);

        for (int i=0; i<length; i++) 
            sb.Append(_base62chars[_random.Next(36)]);

        return sb.ToString();
    }
}

输出:

z5公斤
wd4SUp
美国
UPrGAT
UIf2IS

QCF9GNM5
0UV3TFSS
3MG91VKP
7NTRF10T
AJK3AJU7

3
看起来很棒,有什么不区分大小写的吗?
Kumar

2
如果要避免区分大小写,可以使用基数为36的代码:codeproject.com/Articles/10619/Base-36-type-for-NET-C,但要获得以62为基数的排列,您需要在代码中使用更多字符ID。这是一个权衡。或者,您可以尝试使用除alpha之外的其他字符,但这对用户来说很丑陋。
保罗·萨西克

2
在这里stackoverflow.com/questions/9543892/… 和非常感谢
Kumar

11
一个想法。也许拿出元音以防止意外产生脏话。尤其是面向公众时。
Damien Sawyer 2015年

4
根据您在什么地方使用它(特别是如果希望人类阅读并重新输入代码),您可能需要考虑从考虑中删除经常混淆的字符:0 / O和I / l / 1。在某些情况下,可以通过选择合适的字体来缓解这种情况,但是我无法从问题中得知OP是否可以对此进行控制。
GrandOpener '16

17

我建议http://hashids.org/将任何数字(例如DB ID)转换为字符串(使用salt)。

它允许将此字符串解码回数字。因此,您无需将其存储在数据库中。

具有针对JavaScript,Ruby,Python,Java,Scala,PHP,Perl,Swift,Clojure,Objective-C,C,C ++ 11,Go,Erlang,Lua,Elixir,ColdFusion,Groovy,Kotlin,Nim,VBA, CoffeeScript以及Node.js和.NET。


1
您能否提供与提案类似的其他选项?- - 这很有趣。我想知道PostgreSQL中是否有任何默认选项。
莱奥波德·赫兹(LéoLéopoldHertz),2017年

1
这是它的.NET版本,但是您可以解释它如何工作而不需要将其存储在数据库中吗?我可以只生成唯一的随机数而无需输入数字并且不加盐吗?
shaijut

@Slawa我需要.NET的hashid之类的东西,但最终的哈希将存储在db中具有固定长度的列中,是否可以说总是生成最大长度为N的哈希?
Anon Dev

6

我有与OP类似的要求。我研究了可用的库,但其中大多数是基于随机性的,我不希望如此。我真的找不到任何不是基于随机的但仍然很短的东西...因此,我最终基于Flickr使用的技术进行自己的滚动,但是对其进行了修改,以减少协调并允许更长的离线时间。

简而言之:

  • 中央服务器发布每个包含32个ID的ID块
  • 本地ID生成器维护一组ID块,以在每次请求ID时生成一个ID。当池电量不足时,它将从服务器获取更多ID块以再次填充它。

缺点:

  • 需要中央协调
  • ID或多或少是可预测的(比常规DB ID少,但它们不是随机的)

好处

  • 保持在53位以内(JavaScript / PHP的最大大小为整数)
  • 身份证
  • Base 36编码使人类非常容易阅读,书写和发音
  • 可能需要很长时间在本地生成ID,然后才能再次与服务器联系(取决于池设置)
  • 理论上没有碰撞的机会

我已经发布了用于客户端的Javascript库以及Java EE服务器实现。用其他语言实现服务器也应该很容易。

以下是项目:

suid-简短而甜美的分布式服务唯一ID

suid-server-java -Java EE技术堆栈的Suid-server实现。

这两个库都可以通过自由知识共享开放源代码许可证获得。希望这可以帮助其他人寻找简短的唯一ID。



1
它基于随机数。实际上,这非常棒。但是您的ID不会尽可能的短。我写了SUID以便从1开始编号,因此您将以非常短的ID开始。考虑3个或4个字符。另外,除了以非常短的ID开头之外,具有(大致)递增排序的ID还有其他一些不错的优点。
Stijn de Witt

3

当我为几年前开发的应用程序解决此问题时,我使用了base 36。我需要生成一个人类可读且合理的唯一数字(无论如何在当前日历年之内)。我选择使用从当年1月1日午夜开始的毫秒数(因此,每年的时间戳都可以重复),并将其转换为基数36。如果正在开发的系统遇到致命问题,它将生成基本的36个数字(7个字符),该数字通过Web界面显示给最终用户,然后可以将遇到的问题(和数字)转发给技术支持人员(然后可以使用它在日志中找到堆栈跟踪开始的位置。像56af42g7这样的数字2016-01-21T15:34:29.933-08:00之类的时间戳或5f0d3e0c-da96-11e5-b5d2-0a1d41d68578之类的随机UUID相比,用户阅读和中继更容易。


4
您能否以结构化形式提供有关您的提案的伪代码?听起来很有趣。
莱奥波德·赫兹(LéoLéopoldHertz),2017年

0

我真的很喜欢仅使用Base64格式编码GUID并截断尾随==以获得22个字符的字符串的简便性(它需要一行代码,并且您始终可以将其转换回GUID)。可悲的是,它有时包含+和/字符。对于数据库来说还可以,对于URL来说不是很好,但是它帮助我理解了其他答案:-)

摘自Christiaan van Bergen的https://www.codeproject.com/Tips/1236704/Reducing-the-string-Length-of-a-Guid

我们发现,使用Base64将Guid(16个字节)转换为ASCII表示形式时,会产生仅22个字符可用且仍然唯一的messageID。

var newGuid = Guid.NewGuid();
var messageID = Convert.ToBase64String(newGuid.ToByteArray());

var message22chars = Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Substring(0,22);

例如:Guid'e6248889-2a12-405a-b06d-9695b82c0a9c'(字符串长度:36)将获得Base64表示形式:“ iYgk5hIqWkCwbZaVuCwKnA ==”(字符串长度:24)

Base64表示形式以'=='字符结尾。您可以截断它们,而不会影响唯一性。只剩下22个字符的标识符。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.