8字符随机码

时间:2010-11-04 23:41:34

标签: java python database algorithm unique

我一直在回答几个类似的问题,但找不到我想要的东西。

是否有更有效的方法来生成8个字符的唯一ID,基本36(0-9A-Z),而不是生成唯一ID并查询数据库以查看它是否已存在并重复直到获得唯一ID还没用过?

我发现使用时间的其他解决方案,但这可能太容易猜测,并且在分布式系统中可能无法正常工作。将这些ID视为促销代码。

8 个答案:

答案 0 :(得分:10)

一种选择是以相反的方式执行:在需要时在数据库中生成大量数据,然后在需要时从数据库中获取单个数据,或者为数据库保留一大堆数据。你的特定过程(即在数据库中将它们标记为“可能使用”),然后从内存中将它们分离出来。

答案 1 :(得分:7)

我怀疑你的“低效”方法实际上效率低下。考虑一下:

  • 有36 ^ 8 == 2,821,109,907,456(2.8万亿)可能的身份证。
  • 如果您有N个现有ID,则新的随机生成的ID冲突的可能性为~2.8万亿。
  • 除非N数千亿,否则你“生成一个唯一的ID并查询数据库以查看它是否已经存在”算法几乎总是在一个周期内终止。

通过精心设计,您应该能够在一个数据库请求中生成有保证的唯一ID,几乎在所有时间......除非您拥有大量现有ID。 (如果你这样做,只需在ID中添加另外两个字符,问题就会再次消失。)

如果您愿意,可以通过批量生成ID将平均数据库操作数减少到每个ID少于一个,但它们可能会出现并发症,特别是如果您需要记录实际存在的ID数量使用

但是,如果您最多拥有150,000个ID(我假设,在很长一段时间内生成),那么批量创建ID是不值得的...除非您正在进行批量上传操作。

答案 2 :(得分:1)

不幸的是,8个基数36位有点小。它只有200万可能的ID,所以如果你随机产生140万,你就有大约一半的碰撞机会。

您可以使用具有较长周期的PRNG,并通过某种双射将其当前状态映射到您的ID空间。一个41位的LFSR是不可破解的,但如果你保护的东西不是那么有价值,那么可能是合理的。您可以通过提供具有不同位置的不同节点来开始循环,而无需一直访问数据库。

当然,任何这种确定性方法的问题在于,一旦它被打破,它就会完全被破坏,你就不再相信任何ID了。因此,从数据库中获取数字可能是要走的路,并通过将它们分批发送到一千个或其他任何地方进行分发。

如果您有更大的ID空间,那么您可以使用更安全的技术,例如ID可以包含识别源的内容,该源的递增序列号以及使用源独有的密钥的HMAC

答案 3 :(得分:0)

如果只有一个ID来源(即:您不需要在不同的计算机上协调多个独立的来源),您可以执行以下操作:

  • 计算一个数字可能具有的最大位数,使其不超过8-symbol字符串0-9A-Z中包含的信息。这将是floor(log2(36^8)) = 41位。

  • 让计数器(41位)从零开始

  • 为每个ID请求返回transform(counter++)

transform函数必须是双射的,并且可以是以下操作的任意长序列(当它们以模2^41为模计算时都是双射的):

  • xor,具有固定值
  • 向左或向右旋转固定值
  • 通过固定映射(上面的旋转的推广)对位进行重新排序
  • 添加或减去固定值

完成后,您只需要另一个函数encode(number)即可将数字转换为base36。

答案 4 :(得分:0)

这是一些用于生成随机,base36 ID的python代码。

import random

def base36encode(number, alphabet='0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'):
    '''
    Convert positive integer to a base36 string.

    Source: http://en.wikipedia.org/wiki/Base_36#Python_Conversion_Code
    '''
    if not isinstance(number, (int, long)):
        raise TypeError('number must be an integer')

    # Special case for zero
    if number == 0:
        return '0'

    base36 = ''

    sign   = ""
    if number < 0:
        sign  ='-'
        number=-number

    while number != 0:
        number, i = divmod(number, len(alphabet))
        base36 = alphabet[i] + base36

    return sign + base36

def generateID(length=8):
    '''Generates a base36 ID.'''

    random.seed()
    id = base36encode(random.randint(0, (36**length)-1))

    # append 0s to ensure desired length
    while len(id) < length:
        id = '0' + id

    return id

def generateMultipleIDs(n):
    '''Generate n number of unique, base36 IDs.'''

    output = set()

    while len(output) < n:
        output.add(generateID())

    return output

答案 5 :(得分:0)

我曾经使用涉及较少数量的可能ID的C ++解决了类似的问题,但考虑一些扩展方法可能会有用。基本上我为所有可能的ID创建了一个大位图,并且只是通过测试它的正确位来查找是否正在使用它。

为了最大限度地减少RAM需求,我将位图存储在原始二进制文件中,并使用随机访问文件i / o来查找带有我需要检查和/或设置的相应位的字节。

你的更大的ID空间需要一个328 GB的位图,这可能是不可能的。另一方面,使用的ID的set可能是可接受的,具体取决于您认为实际最终可能使用的ID数。其他替代方案可能是某种sparse file或稀疏矩阵技术,例如scipy.sparse中的那些。

希望这有帮助。

答案 6 :(得分:0)

我做类似的事情来生成激活码:8个字母的小写字符串是一次性的。它们旨在在生成的短时间内使用(通常在几分钟内,但可能不会长达一周),但必须是唯一的。使用它们后,它们将从数据库中删除。

我只是生成一个值,看看它是否在数据库中使用。这现在可以使用了,因为数据库中没有大量未使用的代码,但即使你已经提供了一段代码,仍然不容易猜到。

关于代码:

def _generate_code(self, length):
    random.seed()
    candidates = string.lowercase[:26]
    result = ""
    for i in range(length):
        result += random.choice(candidates)
    return result

答案 7 :(得分:0)

它是否需要加密安全?

如果没有,pc(n)= a + bn,其中b是相对于36 ^ 8的素数。使用byte数组。

foo(int n, byte[] a, byte[] b) {
  byte[] r = new byte[8];
  int carry=0;
  for(int i = 0; i<8;i++) {
    int x = carry + a[i] + n*b[i];
    r[i] = x % 36;
    carry = x / 36;      
  }
}