使用auto_increment生成PHP短唯一ID?

时间:2009-10-30 14:37:24

标签: php mysql unique auto-increment

我想生成一个简短的唯一ID,而不必检查冲突。

我目前做的是这样的,但我目前生成的ID是随机的,在循环中检查冲突很烦人,如果记录数量显着增加,将会变得昂贵。

通常担心碰撞不是问题,但我想要生成的唯一ID是一个短的唯一字符串5-8个字符,字母数字,就像tinyurl一样。

编辑:我想从5个字符开始,如果我达到6千万个条目,那么请转到6 ......等等。

为此,我想我可以使用对用户隐藏的auto_increment值,而是使用MD5或其他方法来显示它们,从而生成一个唯一的字符串。

生成的字符串看起来不应该是线性的,所以简单地将auto_incremented ID转换为base 36 [0-9A-Z]有点过于简单了,但是这样的函数就是我要去的地方此

编辑:安全不是问题,因为这不会用于保护信息。它只是更长字符串的快捷方式。 谢谢。

感谢您的建议,并对此延迟表示歉意。牙医..

8 个答案:

答案 0 :(得分:6)

你需要通过构造正确的东西,即置换函数:这是一个函数,它将一个整数(你的顺序计数器)与另一个整数(你的顺序计数器)进行一对一的可逆映射。 一些例子(这些的任何组合也应该起作用):

  • 反转一些位(f.i.使用XOR,PHP中的^)
  • 交换位数(($ i& 0xc)>> 2 |($ i& 0x3)<< 2),或者只是颠倒所有位的顺序
  • 添加一个以你的最大范围为模的常数值(如果你将它与上面的结果组合,则必须是2的因子)

示例:此函数会将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)

答案 7 :(得分:-1)

递增数字的MD5应该没问题,但是我担心如果你将MD5(通常是128位)截断到5-8个字符,你几乎肯定会损害它作为一个字符串的能力。独特的签名......