【问题标题】:How do I create unique IDs, like YouTube?如何创建唯一 ID,例如 YouTube?
【发布时间】:2010-11-07 17:56:09
【问题描述】:

我一直想知道他们是如何以及为什么这样做的……例如:http://youtube.com/watch?v=DnAMjq0haic

这些 ID 是如何生成的以确保没有重复的,这与使用简单的自动递增数字 ID 相比有什么优势?

如何保持简短但仍保持其独特性? uniqid 创建的字符串很长。

【问题讨论】:

标签: php database


【解决方案1】:

Kevin van Zonneveld 编写了一个出色的article,其中包含一个 PHP 函数来实现这一点。他的方法是我在研究这个主题时发现的最好的方法。

他的功能很聪明。它使用固定的 $index 变量,因此可以删除有问题的字符(例如元音,或避免 O 和 0 混淆)。它还有一个混淆 id 的选项,这样它们就不容易被猜到。

【讨论】:

  • 我也将发布相同的文章作为答案。这是我认为最好的解决方案。提问者,请接受 1 作为答案
【解决方案2】:

这是一个每次随机生成唯一密钥的小函数。重复相同唯一 ID 的机会非常少。

function uniqueKey($limit = 10) {
    $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $randstring = '';
    for ($i = 0; $i < $limit; $i++) {
        $randstring .= $characters[rand(0, strlen($characters))];
    }
    return $randstring;
}

来源:generate random unique IDs like YouTube or TinyURL in PHP

【讨论】:

    【解决方案3】:

    试试这个:http://php.net/manual/en/function.uniqid.php

    uniqid — 生成唯一 ID...

    根据当前时间(以微秒为单位)获取带前缀的唯一标识符。

    注意 此函数不会生成加密安全值,并且不应用于加密目的。如果您需要加密安全值,请考虑改用 random_int()random_bytes()openssl_random_pseudo_bytes()

    警告 该函数不保证返回值的唯一性。由于大多数系统通过 NTP 或类似的方式调整系统时钟,因此系统时间不断变化。因此,此函数可能不会返回进程/线程的唯一 ID。使用more_entropy 增加唯一性的可能性...

    【讨论】:

      【解决方案4】:

      这是不是 PHP,但可以转换为 php 或 Javascript 等 clinetside 而无需减慢服务器速度。它可以在您发布任何需要唯一 ID 的内容时使用你的 php。

      这是一种创建唯一 ID 的方法,仅限于

      9 007 199 254 740 992 unique id's
      

      它总是返回 9 个字符。

      其中iE2XnNGpF9 007 199 254 740 992

      您可以编码一个长的Number,然后解码生成的9char String 并返回数字。

      基本上这个函数使用 62base 索引 Math.log() 和 Math.Power 根据数字获得正确的索引。我会解释更多关于这个函数的信息,但我前段时间找到了它并且找不到该站点我花了很长时间才知道它是如何工作的……无论如何,我从 0.. 重写了这个函数,这个函数比我找到的那个快 2-3 倍。 我循环了 1000 万次,检查该数字是否与 enc dec 进程相同,这个过程耗时 33 秒,另一个耗时 90 秒。

      var UID={
       ix:'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ',
       enc:function(N){
        N<=9007199254740992||(alert('OMG no more uid\'s'));
        var M=Math,F=M.floor,L=M.log,P=M.pow,r='',I=UID.ix,l=I.length,i;
        for(i=F(L(N)/L(l));i>=0;i--){
         r+=I.substr((F(N/P(l,i))%l),1)
        };
        return UID.rev(new Array(10-r.length).join('a')+r)
       },
       dec:function(S){
        var S=UID.rev(S),r=0,i,l=S.length,I=UID.ix,j=I.length,P=Math.pow;
        for(i=0;i<=(l-1);i++){r+=I.indexOf(S.substr(i,1))*P(j,(l-1-i))};
        return r
       },
       rev:function(a){return a.split('').reverse().join('')}
      };
      

      因为我想要一个 9 字符的字符串,所以我还在生成的字符串上附加了 a's,它们是 0's。

      要对数字进行编码,您需要传递 Number 而不是字符串。

      var uniqueId=UID.enc(9007199254740992);
      

      要再次解码数字,您需要传递生成的 9char String

      var id=UID.dec(uniqueId);
      

      这里有一些数字

      console.log(UID.enc(9007199254740992))//9 biliardi o 9 milioni di miliardi
      console.log(UID.enc(1)) //baaaaaaaa 
      console.log(UID.enc(10)) //kaaaaaaaa 
      console.log(UID.enc(100)) //Cbaaaaaaa 
      console.log(UID.enc(1000)) //iqaaaaaaa 
      console.log(UID.enc(10000)) //sBcaaaaaa 
      console.log(UID.enc(100000)) //Ua0aaaaaa 
      console.log(UID.enc(1000000)) //cjmeaaaaa
      console.log(UID.enc(10000000)) //u2XFaaaaa
      console.log(UID.enc(100000000)) //o9ALgaaaa 
      console.log(UID.enc(1000000000)) //qGTFfbaaa
      console.log(UID.enc(10000000000)) //AOYKUkaaa 
      console.log(UID.enc(100000000000)) //OjO9jLbaa
      console.log(UID.enc(1000000000000)) //eAfM7Braa 
      console.log(UID.enc(10000000000000)) //EOTK1dQca
      console.log(UID.enc(100000000000000)) //2ka938y2a
      

      如您所见,有很多a,而您不希望这样...所以从一个高数字开始。 假设您的数据库 ID 为 1 .. 只需添加 100000000000000 即可获得 100000000000001

      而且你的唯一 ID 看起来像 youtube 的 ID 3ka938y2a

      我认为满足其他 8907199254740992 唯一 ID 并不容易

      【讨论】:

        【解决方案5】:

        我遇到了类似的问题 - 我在数据库中有主 ID,但我不想将它们暴露给用户 - 显示某种哈希值会更好。所以,我写了 hashids。

        文档:http://www.hashids.org/php/

        来源:https://github.com/ivanakimov/hashids.php

        使用此类创建的哈希是唯一且可解密的。您可以提供自定义的盐值,这样其他人就无法解密您的哈希(不是说这是一个大问题,但仍然是一个“值得拥有”)。

        要加密一个数字,你会这样做:

        require('lib/Hashids/Hashids.php');
        
        $hashids = new Hashids\Hashids('this is my salt');
        $hash = $hashids->encrypt(123);
        

        您的$hash 现在是:YDx

        您还可以将最小哈希长度设置为构造函数的第二个参数,以便您的哈希可以更长。或者,如果您有一个复杂的集群系统,您甚至可以将多个数字加密到一个哈希中:

        $hash = $hashids->encrypt(2, 456); /* aXupK */
        

        (例如,如果您在集群 2 中有一个用户和一个主 ID 456 的对象)解密的工作方式相同:

        $numbers = $hashids->decrypt('aXupK');
        

        $numbers 将是:[2, 456]

        这样做的好处是您甚至不必将这些哈希值存储在数据库中。一旦请求进入,您就可以从 url 获取哈希并即时解密 - 然后从数据库中提取主 ID(这显然是速度上的优势)。

        与输出相同 - 您可以在输出时加密 id,并向用户显示哈希。

        编辑

        1. 更改了网址以包含文档网站和代码源
        2. 更改示例代码以适应主库更新(当前 PHP 库版本为 0.3.0 - 感谢所有开源社区对库的改进)

        【讨论】:

        • 我将 youtube 网址的“DnAMjq0haic”部分称为哈希。并且所提供的盐值是独一无二的。你要解释吗?
        • 我不认为你可以解密“哈希”,根据定义它们是单向函数,即,容易在一个方向上生成并且极难反转。
        【解决方案6】:

        base62 或 base64 对主键的值进行编码,然后将其存储在另一个字段中。

        主键 12443 = 3eH 的 base62 示例

        节省一些空间,这就是为什么我确定 youtube 正在使用它。

        对您的 PK 或唯一标识符进行 base62(A-Za-z0-9) 编码将避免检查密钥是否已存在的开销:)

        【讨论】:

          【解决方案7】:

          一种方法是使用每次唯一输入的哈希函数。

          示例(您已经用 php 标记了问题):

          $uniqueID = null
          do {
            $uniqueID = sha1( $fileName + date() );
          } while ( !isUnique($uniqueID) )
          

          【讨论】:

          • 不是 保证 唯一的 :) 不过不太可能发生冲突。一个完整的解决方案至少会检查冲突,如果有冲突,则重新生成一个新 ID。
          • 你完全正确我最近为一个应用程序实现了它,忘记在这里写了!让我们重构 anwser...
          • 在这种情况下不太可能需要大量的计算才能找到两个完全碰撞的。即使假设需要 2^63 个散列左右,也需要通过授予密码分析攻击模型散列函数中的微不足道的冗余来获得 50% 的冲突机会。数字很​​大:9.22337204 × 10^18,所以如果你用这个生成约 40 亿个密钥,你仍然只有 40 亿分之一的机会在它们之间发生冲突。而且,如果您不承认加密漏洞会直接增加碰撞机会,那么这些可能性对您来说更有利。
          • @Edward:仍然值得额外的两行代码 :) @dimitris:你的意思是 !unique,不是吗?
          • @Mark:直到您在分布式系统中生成它并且只有快照一致性保证,因为您无法跟上负载,我同意。 ;)
          【解决方案8】:

          如果您想要短网址并且不担心可预测性,您可以convert the auto-incrementing ID to a higher base

          【讨论】:

            【解决方案9】:

            像 SHA-1 或 MD5 和 GUID 这样的哈希函数的结果往往会变得很长,这可能是您不想要的。 (您特别提到了 YouTube 作为示例:即使他们托管的视频数不胜数,它们的标识符也相对较短。)

            这就是为什么在将数字 ID 放入 URL 时,您可能希望考虑将在幕后使用的数字 ID 转换为另一个基数。 Flickr 例如使用 Base58 作为其规范的短 URL。有关此的详细信息可在此处获得:http://www.flickr.com/groups/api/discuss/72157616713786392/。如果您正在寻找通用解决方案,请查看 PEAR 包 Math_Basex。

            请注意,即使在另一个基础上,仍然可以从您的应用程序外部预测 ID。

            【讨论】:

              【解决方案10】:

              考虑使用类似的东西:

              $id = base64_encode(md5(uniqid(),true));

              uniqid 将为您提供一个唯一标识符。 MD5 会扩散它给你一个 128 位的结果。 Base 64 编码将为您提供适合在网络上使用的标识符中的每个字符 6 位,重约 23 个字符并且在计算上难以猜测。如果您想更加偏执,请从 md5 升级到 sha1 或更高。

              【讨论】:

                【解决方案11】:

                我没有公式,但我们在我正在进行的项目中这样做。 (我不能分享它)。但我们基本上一次生成一个字符并附加字符串。

                一旦我们有了一个完整的字符串,我们就会根据数据库检查它。如果没有其他的,我们就去吧。如果它是重复的,我们重新开始这个过程。不是很复杂。

                优点是,我猜是GUID

                【讨论】:

                • 它似乎比增量 ID 更不独特。如果您有一个由 5 台机器组成的集群绑定到 5 个 DB 怎么办? 10? uuid 绑定到硬件,这是一个很大的优势。它总是独一无二的,即使我们在类似的机器上同时生成它。
                • 一旦你的数据库饱和,这个算法会很慢。我之前考虑过这条路线,所以我希望你们能预料到这一点,一旦达到拐点,您可能希望通过增加空白空间来预生成其余的或健康数量的前瞻值。
                【解决方案12】:

                这在很大程度上取决于您需要做什么。 “独特”有多独特?您是否提供了唯一 ID,它们在您的数据库中是否意味着什么?如果是这样,顺序 # 可能没问题。

                另一方面,如果您使用顺序 #,则有人可以通过遍历数字系统地窃取您的内容。

                有一些文件系统命令会生成唯一的文件名——你可以使用它们。

                或 GUID。

                【讨论】:

                • GUID 如果您不要求它们不可预测,则可以很好地工作。但是,由于它们的大部分非冲突保证来自众所周知的部分,它们来自 MAC 地址等。您明确不能依赖它们没有可猜测的进展。
                • @Edward Kmett:UUID(其中 GUID 是特定子集的实现)可以使用几种不同的算法,其中只有一种使用 MAC 地址或其他知名来源。甚至有一个几乎所有位都来自随机来源。
                • 尝试随机生成的版本 4 UUID。 en.wikipedia.org/wiki/…
                • 啊,好点。 122 位随机善良。如果您确定用于获取 UUID 的内容正在生成随机生成的 UUID,我不会抱怨,我刚刚意识到 Microsoft 的 NewGuid() 确实如此。很高兴知道。嗯。有趣的是,SQL Server 中的 NEWSEQUENTIALID() 调用不对应于任何已定义的 UUID 或 GUID 格式(它似乎属于 Wikipedia 的 GUID 规范中的“保留用于进一步扩展”)可能是因为它比它们中的任何一个都弱。 ;)
                【解决方案13】:

                应该有一个 PHP 库来生成这些 ID。如果没有,实现起来也不难。

                优点是以后在尝试重新组织或合并不同的服务器资源时不会出现名称冲突。使用数字 ID,您必须更改其中一些以解决冲突,这将导致 Url 更改导致 SEO 命中。

                【讨论】:

                • -1 因为懒得谷歌“php guid”并找到合适的函数,+2 用于提及服务器冲突。我确信谷歌使用某种服务器云,这可能是他们在 youtube 上使用类似 ID 的一个重要原因。
                • Mark:实际上我不一定会推荐 GUID。它们被设计为无碰撞,但不难预测,因此要满足这些标识符的所有期望特征,您可能需要更强大的东西。
                • 我以为 Mastermind 在他的第一句话中指的是 GUID。并不意味着它们是一个很好的解决方案:D
                【解决方案14】:

                自动递增很容易被抓取。这些无法预测,因此无法顺序抓取。

                我建议使用双 URL 格式(类似于 SO URL):

                yoursite.com/video_idkey/url_friendly_video_title
                

                如果您需要 id 和 url 中的标题,则可以使用简单的数字,例如 0001、0002、0003 等。

                生成这些密钥非常简单。您可以使用 PHP 中的uniqid() 函数生成 13 个字符,或者生成 23 个具有更多熵的字符。

                【讨论】:

                • 不过就是这样...... SO的URL方案根本不需要标题,看:stackoverflow.com/questions/1076110
                • 如果您担心抓取,您可以强制它需要这两个部分。
                • 我知道 - 你也不必要求它。但如果你想让他们不爬 1-1,000,000,也需要标题。
                • 爬取有什么问题?无论如何,您不希望用户看到的任何页面都应该有适当的保护......
                猜你喜欢
                • 2022-11-28
                • 1970-01-01
                • 2022-08-04
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2011-08-14
                • 2010-11-26
                相关资源
                最近更新 更多