【发布时间】:2010-11-07 17:56:09
【问题描述】:
我一直想知道他们是如何以及为什么这样做的……例如:http://youtube.com/watch?v=DnAMjq0haic
这些 ID 是如何生成的以确保没有重复的,这与使用简单的自动递增数字 ID 相比有什么优势?
如何保持简短但仍保持其独特性? uniqid 创建的字符串很长。
【问题讨论】:
我一直想知道他们是如何以及为什么这样做的……例如:http://youtube.com/watch?v=DnAMjq0haic
这些 ID 是如何生成的以确保没有重复的,这与使用简单的自动递增数字 ID 相比有什么优势?
如何保持简短但仍保持其独特性? uniqid 创建的字符串很长。
【问题讨论】:
Kevin van Zonneveld 编写了一个出色的article,其中包含一个 PHP 函数来实现这一点。他的方法是我在研究这个主题时发现的最好的方法。
他的功能很聪明。它使用固定的 $index 变量,因此可以删除有问题的字符(例如元音,或避免 O 和 0 混淆)。它还有一个混淆 id 的选项,这样它们就不容易被猜到。
【讨论】:
这是一个每次随机生成唯一密钥的小函数。重复相同唯一 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
【讨论】:
试试这个:http://php.net/manual/en/function.uniqid.php
uniqid — 生成唯一 ID...
根据当前时间(以微秒为单位)获取带前缀的唯一标识符。
注意 此函数不会生成加密安全值,并且不应用于加密目的。如果您需要加密安全值,请考虑改用 random_int()、random_bytes() 或 openssl_random_pseudo_bytes()。
警告 该函数不保证返回值的唯一性。由于大多数系统通过 NTP 或类似的方式调整系统时钟,因此系统时间不断变化。因此,此函数可能不会返回进程/线程的唯一 ID。使用
more_entropy增加唯一性的可能性...
【讨论】:
这是不是 PHP,但可以转换为 php 或 Javascript 等 clinetside 而无需减慢服务器速度。它可以在您发布任何需要唯一 ID 的内容时使用你的 php。
这是一种创建唯一 ID 的方法,仅限于
9 007 199 254 740 992 unique id's
它总是返回 9 个字符。
其中iE2XnNGpF 是9 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 并不容易
【讨论】:
我遇到了类似的问题 - 我在数据库中有主 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,并向用户显示哈希。
编辑:
【讨论】:
base62 或 base64 对主键的值进行编码,然后将其存储在另一个字段中。
主键 12443 = 3eH 的 base62 示例
节省一些空间,这就是为什么我确定 youtube 正在使用它。
对您的 PK 或唯一标识符进行 base62(A-Za-z0-9) 编码将避免检查密钥是否已存在的开销:)
【讨论】:
一种方法是使用每次唯一输入的哈希函数。
示例(您已经用 php 标记了问题):
$uniqueID = null
do {
$uniqueID = sha1( $fileName + date() );
} while ( !isUnique($uniqueID) )
【讨论】:
如果您想要短网址并且不担心可预测性,您可以convert the auto-incrementing ID to a higher base。
【讨论】:
像 SHA-1 或 MD5 和 GUID 这样的哈希函数的结果往往会变得很长,这可能是您不想要的。 (您特别提到了 YouTube 作为示例:即使他们托管的视频数不胜数,它们的标识符也相对较短。)
这就是为什么在将数字 ID 放入 URL 时,您可能希望考虑将在幕后使用的数字 ID 转换为另一个基数。 Flickr 例如使用 Base58 作为其规范的短 URL。有关此的详细信息可在此处获得:http://www.flickr.com/groups/api/discuss/72157616713786392/。如果您正在寻找通用解决方案,请查看 PEAR 包 Math_Basex。
请注意,即使在另一个基础上,仍然可以从您的应用程序外部预测 ID。
【讨论】:
考虑使用类似的东西:
$id = base64_encode(md5(uniqid(),true));
uniqid 将为您提供一个唯一标识符。 MD5 会扩散它给你一个 128 位的结果。 Base 64 编码将为您提供适合在网络上使用的标识符中的每个字符 6 位,重约 23 个字符并且在计算上难以猜测。如果您想更加偏执,请从 md5 升级到 sha1 或更高。
【讨论】:
我没有公式,但我们在我正在进行的项目中这样做。 (我不能分享它)。但我们基本上一次生成一个字符并附加字符串。
一旦我们有了一个完整的字符串,我们就会根据数据库检查它。如果没有其他的,我们就去吧。如果它是重复的,我们重新开始这个过程。不是很复杂。
优点是,我猜是GUID。
【讨论】:
这在很大程度上取决于您需要做什么。 “独特”有多独特?您是否提供了唯一 ID,它们在您的数据库中是否意味着什么?如果是这样,顺序 # 可能没问题。
另一方面,如果您使用顺序 #,则有人可以通过遍历数字系统地窃取您的内容。
有一些文件系统命令会生成唯一的文件名——你可以使用它们。
或 GUID。
【讨论】:
应该有一个 PHP 库来生成这些 ID。如果没有,实现起来也不难。
优点是以后在尝试重新组织或合并不同的服务器资源时不会出现名称冲突。使用数字 ID,您必须更改其中一些以解决冲突,这将导致 Url 更改导致 SEO 命中。
【讨论】:
自动递增很容易被抓取。这些无法预测,因此无法顺序抓取。
我建议使用双 URL 格式(类似于 SO URL):
yoursite.com/video_idkey/url_friendly_video_title
如果您需要 id 和 url 中的标题,则可以使用简单的数字,例如 0001、0002、0003 等。
生成这些密钥非常简单。您可以使用 PHP 中的uniqid() 函数生成 13 个字符,或者生成 23 个具有更多熵的字符。
【讨论】: