代码之家  ›  专栏  ›  技术社区  ›  Daren Schwenke

使用自动增量生成PHP短唯一ID?

  •  7
  • Daren Schwenke  · 技术社区  · 16 年前

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

    我目前正在做类似的事情,但是我当前生成的ID是随机的,在一个循环中检查冲突是很烦人的,如果记录的数量显著增加,将会很昂贵。

    通常,担心冲突不是问题,但我想生成的唯一ID是一个短的唯一字符串5-8个字符,字母数字,和tinyurl一样。

    编辑:我想从5个字符开始,如果我点击了6000万个条目,那么转到6。等等。

    为此,我认为我可以使用一个对用户隐藏的自动增量值,并用一个 MD5 或者其他方法,从中生成唯一的字符串。

    生成的字符串不应该是线性的,因此只需将自动递增的ID转换为 base 36 [0-9a-z]有点过于简单化,但类似这样的函数正是我要处理的地方。

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

    谢谢你的建议,很抱歉耽误了时间。牙医。。

    8 回复  |  直到 16 年前
        1
  •  6
  •   Wim    16 年前

    您需要一些结构上正确的东西,例如置换函数:这是一个函数,它对一个整数(顺序计数器)进行一对一的可逆映射到另一个整数。 一些例子(这些的任何组合都应该有效):

    • 反转一些位(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
    

    为了 good values A、C和M的顺序是x_0、x_1。x_m-1将只包含一次0和m-1之间的所有数字。现在可以从线性递增的索引开始,并使用 下一个 在LCG序列中作为“秘密”密钥的值。

    编辑2

    实施: 你可以 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)
    
        2
  •  1
  •   Aishwar    16 年前

    您可能会生成当前日期时间/随机数的MD5哈希,并将其截断到所需的长度(5-8个字符),然后将其存储为ID字段。

    如果您使用将这些信息存储在数据库中,则不需要使用for循环来执行冲突检查,但可以只执行select语句-类似于

    SELECT count(1) c FROM Table WHERE id = :id
    

    其中:id是新生成的id。如果c大于0,则说明它已经存在。

    编辑

    这可能不是最好的方法。但我会给它一个机会,所以我猜你需要的是某种方法,把一个数字转换成一个唯一的短字符串,而不是按顺序。

    我猜就像你说的,base64编码已经完成了从数字到短字符串的转换。为了避免序列问题,您可以在自动生成的ID与某个“随机”值(唯一映射)之间进行一些映射。然后您可以base64编码这个唯一的值。

    您可以如下生成这个映射。具有1-10000000之间的临时表存储值。按随机顺序排序,并将其存储到映射表中。

    INSERT INTO MappingTable (mappedId) SELECT values FROM TemporaryTable ORDER BY RAND()
    

    其中,mappingtable将具有2个字段id(您的自动生成的id将对照该字段查找)和mappedid(您将为其生成base64编码)。

    当您接近10000000时,您可以再次运行上述代码,并用10000001-20000000或类似的值更改临时表中的值。

        3
  •  1
  •   longneck    16 年前

    您可以使用位异或对某些位进行加扰:

    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 |
    +-----+---------+
    
        4
  •  0
  •   Pekka    16 年前

    我认为这永远不会是真正安全的,因为您只需要在短的唯一字符串后面找到加密方法就可以劫持一个ID。在您的设置中,检查循环中的冲突真的有那么大的问题吗?

        5
  •  0
  •   Wim    16 年前

    一个递增数字的MD5 应该很好,但我担心如果 您正在截断MD5(即 通常为128位)降到5-8 人物,你几乎肯定 破坏它的能力 唯一的签名…

    完全正确。尤其是当你达到80%的碰撞概率时,一个被截断的MD5将和任何一个随机数一样好,以保证它本身的唯一性,即没有价值。

    但既然您使用的是数据库,为什么不使用唯一索引呢?通过这种方式,MySQL本身就完成了唯一性检查(比使用循环更有效)。只需尝试用MD5生成的密钥进行插入,如果失败,请重试…

        6
  •  0
  •   Kevin Peno    16 年前

    如果不能使用自动递增字段,并且需要 绝对地 唯一值,使用 UUID . 如果您决定使用任何其他方法(除了自动增量),那么不检查碰撞是很愚蠢的。

        7
  •  0
  •   bender    16 年前
        8
  •  -1
  •   dicroce    16 年前

    一个递增数字的MD5应该是可以的,但我担心如果您将MD5(通常是128位)截断为5-8个字符,您几乎肯定会破坏它作为唯一签名的能力…

    推荐文章