我有一个Django Web应用程序,它使用默认的自动递增正整数作为主键.此密钥在整个应用程序中使用,并经常插入到URL中.我不想向公众公开这个号码,以便他们可以猜测我的数据库中的用户或其他实体的数量.
这是一个经常要求,我已经向类似的矿山看到了问题和答案.大多数解决方案建议散列原始主键值.但是,这些答案都不符合我的需要.这些是我的要求:
>我想将主键字段类型保持为整数.
>我也不希望每次读取或写入或与数据库进行比较时都不必散列/取消该值.这看起来很浪费这样做一次会很好:当记录最初插入数据库时
>哈希/加密功能不需要是可逆的,因为我不需要恢复原始顺序密钥.散列值只需要是唯一的.
>散列值只需对于该表是唯一的 – 不是普遍唯一的.
>散列值应尽可能短.我想避免使用极长(20个字符)的网址
实现这一目标的最佳方法是什么?以下工作会怎样?
def hash_function(int):
return fancy-hash-function # What function should I use??
def obfuscate_pk(sender,instance,created,**kwargs):
if created:
logger.info("MyClass #%s,created with created=%s: %s" % (instance.pk,instance))
instance.pk = hash_function(instance.pk)
instance.save()
logger.info("\tNew Pk=%s" % instance.pk)
class MyClass(models.Model):
blahblah = models.CharField(max_length=50,null=False,blank=False,)
post_save.connect(obfuscate_pk,sender=MyClass)
我会向您推荐Instragam使用的相同方法.他们的要求似乎紧跟您的要求.
Generated IDs should be sortable by time (so a list of photo IDs,for
example,could be sorted without fetching more information about the
photos) IDs should ideally be 64 bits (for smaller indexes,and better
storage in systems like Redis) The system should introduce as few new
‘moving parts’ as possible—a large part of how we’ve been able to
scale Instagram with very few engineers is by choosing simple,
easy-to-understand solutions that we trust.
他们提出了一个基于时间戳的41位系统,数据库分片为13位,自动增量部分为10位. Sincce你似乎没有使用分片.对于基于时间的copmonent,您可以只有41位,随机选择23位.如果您同时插入记录,那么确实会产生极不可能的8百万分之一的冲突机会.但在实践中,你永远不可能达到这个目的.那么一些代码怎么样:
生成ID
START_TIME = a constant that represents a unix timestamp
def make_id():
'''
inspired by http://instagram-engineering.tumblr.com/post/10853187575/sharding-ids-at-instagram
'''
t = int(time.time()*1000) - START_TIME
u = random.SystemRandom().getrandbits(23)
id = (t << 23 ) | u
return id
def reverse_id(id):
t = id >> 23
return t + START_TIME
注意,上面代码中的START_TIME是一些任意的开始时间.您可以使用time.time()* 1000,获取值并将其设置为START_TIME
请注意,我发布的reverse_id方法允许您找出创建记录的时间.如果您需要跟踪这些信息,您可以这样做而无需为其添加其他字段!所以你的主键实际上是保存你的存储而不是增加它!
该模型
现在这就是你的模型的样子.
class MyClass(models.Model):
id = models.BigIntegerField(default = fields.make_id,primary_key=True)