一尘不染

如何用该表唯一的另一个整数替换Django的主键

mysql

我有一个Django
Web应用程序,它使用默认的自动递增正整数作为主键。该密钥在整个应用程序中使用,并且经常插入URL中。我不想向公众公开此数字,以便他们可以猜测数据库中用户或其他实体的数量。

这是一个常见的要求,我已经看到类似的问题并给出了答案。大多数解决方案建议对原始主键值进行哈希处理。但是,这些答案都无法完全满足我的需求。这些是我的要求:

  1. 我想将主键字段类型保持为整数。
  2. 我也不希望每次读取或写入此值或将其与数据库进行比较时都不必对该值进行哈希/取消哈希处理。这似乎很浪费,只需执行一次即可:将记录最初插入数据库时
  3. 哈希/加密功能不需要是可逆的,因为我不需要恢复原始的顺序密钥。哈希值只需要是唯一的。
  4. 哈希值仅需对该表唯一,而不是通用唯一。
  5. 哈希值应尽可能短。我想避免使用过长的网址(超过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, created, 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)

阅读 205

收藏
2020-05-17

共1个答案

一尘不染

想法

我会向您推荐Instragam所使用的相同方法。他们的要求似乎紧随您的要求。

生成的ID应该可以按时间排序(例如,可以在不获取有关照片的更多信息的情况下对照片ID列表进行排序)ID理想情况下应为64位(以用于较小的索引,并更好地存储在Redis等系统中)应该引入尽可能少的新“活动部件”-我们能够通过很少的工程师扩展Instagram的很大一部分是通过选择我们信任的简单易懂的解决方案。

他们提出了一个基于时间戳的41位系统,13个数据库分片和10个自动增量部分系统。既然您似乎没有使用分片。对于基于时间的共模,您只能有41位,而随机选择只有23位。如果同时插入记录,则确实极不可能发生冲突的可能性是830万分之一。但是实际上,您永远不可能实现这一目标。对,那么一些代码呢:

生成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)

如果您在django之外对数据库进行更改,则需要创建与make_idsql函数等效的

作为脚注。这有点像Mongodb
用于为每个对象生成_ID的方法。

2020-05-17