我一直在回答几个类似的问题,但找不到我想要的东西。
是否有更有效的方法来生成8个字符的唯一ID,基本36(0-9A-Z),而不是生成唯一ID并查询数据库以查看它是否已存在并重复直到获得唯一ID还没用过?
我发现使用时间的其他解决方案,但这可能太容易猜测,并且在分布式系统中可能无法正常工作。将这些ID视为促销代码。
答案 0 :(得分:10)
一种选择是以相反的方式执行:在需要时在数据库中生成大量数据,然后在需要时从数据库中获取单个数据,或者为数据库保留一大堆数据。你的特定过程(即在数据库中将它们标记为“可能使用”),然后从内存中将它们分离出来。
答案 1 :(得分:7)
我怀疑你的“低效”方法实际上效率低下。考虑一下:
通过精心设计,您应该能够在一个数据库请求中生成有保证的唯一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位)从零开始
transform(counter++)
transform
函数必须是双射的,并且可以是以下操作的任意长序列(当它们以模2^41
为模计算时都是双射的):
完成后,您只需要另一个函数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;
}
}