我想生成一个简短的唯一ID,而不必检查冲突。
我目前做的是这样的,但我目前生成的ID是随机的,在循环中检查冲突很烦人,如果记录数量显着增加,将会变得昂贵。
通常担心碰撞不是问题,但我想要生成的唯一ID是一个短的唯一字符串5-8个字符,字母数字,就像tinyurl一样。
编辑:我想从5个字符开始,如果我达到6千万个条目,那么请转到6 ......等等。
为此,我想我可以使用对用户隐藏的auto_increment值,而是使用MD5
或其他方法来显示它们,从而生成一个唯一的字符串。
生成的字符串看起来不应该是线性的,所以简单地将auto_incremented ID转换为base 36
[0-9A-Z]有点过于简单了,但是这样的函数就是我要去的地方此
编辑:安全不是问题,因为这不会用于保护信息。它只是更长字符串的快捷方式。 谢谢。
感谢您的建议,并对此延迟表示歉意。牙医..
答案 0 :(得分:6)
你需要通过构造正确的东西,即置换函数:这是一个函数,它将一个整数(你的顺序计数器)与另一个整数(你的顺序计数器)进行一对一的可逆映射。 一些例子(这些的任何组合也应该起作用):
示例:此函数会将0,1,2,3,5,..转换为13,4,12,7,15,..,数字最多为15:
$i=($input+97) & 0xf;
$result=((($i&0x1) << 3) + (($i&0xe) >> 1)) ^ 0x5;
修改强>
更简单的方法是使用线性同余生成器(LCG,通常用于生成随机数),它由以下形式的公式定义:
X_n+1 = (a * X_n + c) mod m
对于a,c和m的good values,X_0,X_1 .. X_m-1的序列将包含0到m-1之间的所有数字恰好一次。现在,您可以从线性增加的索引开始,并使用LCG序列中的 next 值作为“秘密”密钥。
<强> EDIT2 强>
实现: 你可以design your own LCG parameters,但是如果你弄错了它将不会涵盖整个范围(因此有重复),所以我将使用this paper中已发布和尝试过的一组参数:
a = 16807, c = 0, m = 2147483647
这给你一个2 ** 31的范围。使用pack(),您可以将结果整数作为字符串,base64_encode()使其成为可读字符串(最多6个有效字符,每字节6位),这可能是您的功能:
substr(base64_encode(pack("l", (16807 * $index) % 2147483647)), 0, 6)
答案 1 :(得分:1)
您可能会生成当前日期时间/随机数的MD5哈希值,并将其截断为您需要的长度(5-8个字符)并将其存储为id字段。
如果您正在使用将此信息存储在数据库中,则不需要使用for循环来执行冲突检查,但您可以执行select语句 - 类似于
SELECT count(1) c FROM Table WHERE id = :id
其中:id是新生成的id。如果c大于0,那么你知道它已经存在。
修改强>
这可能不是最佳方式。但我会试一试,所以我猜你需要的是将数字转换成一个独特的短字符串而不是顺序。
我想如你所说,base64编码已经将数字转换为短字符串。为了避免序列问题,您可以在自动生成的id与某些“随机”值(唯一映射)之间进行一些映射。然后你可以对这个唯一值进行base64编码。
您可以按如下方式生成此映射。临时表存储的值为1 - 10,000,000。按随机顺序对其进行排序并将其存储到Map表中。
INSERT INTO MappingTable (mappedId) SELECT values FROM TemporaryTable ORDER BY RAND()
其中MappingTable将具有2个字段id(您的自动生成的id将查找此对象)和mappedId(您将为其生成base64编码)。
当你接近10,000,000时,你可以再次重新运行上面的代码并用10,000,001-20,000,000或类似的东西更改临时表中的值。
答案 2 :(得分:1)
你可以使用按位异或来加扰某些位:
select thefield ^ 377 from thetable;
+-----+---------+
| a | a ^ 377 |
+-----+---------+
| 154 | 483 |
| 152 | 481 |
| 69 | 316 |
| 35 | 346 |
| 72 | 305 |
| 139 | 498 |
| 96 | 281 |
| 31 | 358 |
| 11 | 370 |
| 127 | 262 |
+-----+---------+
答案 3 :(得分:0)
我认为这永远不会真正安全,因为你只需要在短的唯一字符串后面找到加密方法来劫持ID。检查环路中的碰撞确实在您的设置中存在问题吗?
答案 4 :(得分:0)
递增数字的MD5 应该没问题,但我担心如果 你正在截断你的MD5(这是 通常是128位)低至5-8 人物,你几乎可以肯定 破坏它的行为能力 一个独特的签名......
完全正确。特别是如果你达到80%的碰撞几率,截断的MD5将与任何随机数一样好,以保证其本身的唯一性,即毫无价值。
但是既然你正在使用数据库,为什么不使用UNIQUE INDEX呢?这样,MySQL自身完成了单一检查(以比使用循环更有效的方式)。只是尝试使用MD5生成的密钥进行INSERT,如果失败,请再试一次......
答案 5 :(得分:0)
如果您无法使用自动增量字段,并且想要绝对唯一值,请使用UUID。如果你决定使用其他任何东西(除了自动增量),你也不会检查是否有碰撞。
答案 6 :(得分:0)
此博客文章与您所追求的内容非常接近。
http://kevin.vanzonneveld.net/techblog/article/create_short_ids_with_php_like_youtube_or_tinyurl/
答案 7 :(得分:-1)
递增数字的MD5应该没问题,但是我担心如果你将MD5(通常是128位)截断到5-8个字符,你几乎肯定会损害它作为一个字符串的能力。独特的签名......