为什么将分片ID附加到生成的ID?

时间:2019-04-27 17:46:11

标签: database facebook instagram sharding id-generation

我正在阅读: https://instagram-engineering.com/sharding-ids-at-instagram-1cf5a71e5a5c

在上一节“解决方案”中,他们将根据数据库的自动增量功能+历元以来的毫秒数+分片ID生成全局唯一的ID。

我们为什么需要在其中添加分片ID?

具体说来

  

接下来,我们获取此特定数据的分片ID   试图插入。假设我们要通过用户ID分片,   2000个逻辑分片;如果我们的用户ID为31341,则分片ID为   31341%2000->1341。我们用该值填充接下来的13位

这没有任何意义:如果您已经通过分片数量(31341%2000)来修改用户ID,则意味着1)您已经具有用户ID! 2)您已经知道mod功能所属的分片了!

我在这里误会什么?

2 个答案:

答案 0 :(得分:4)

也许我可以为您更好地分解它,但这不是 ,因为user-id不适合。

他们正在使用Twitter Snowflake ID。它旨在并行生成多个服务器,多个数据中心之间的唯一ID。例如,在相同的确切瞬间,两个“位置”中的两个“项目”需要一个相距不到一毫秒(甚至在同一纳秒内)的东西的有保证的唯一ID。此唯一ID具有以下要求:需要以极快的速度生产,高效,以可以有效解析的逻辑方式构建,可以容纳64位以内,并且如果ID在许多人的生命周期内,生成它的方法需要能够处理大量的数据。这意味着他们无法进行数据库查找以获得尚未使用的唯一ID,无法确定生成的ID在确定后是否为唯一,并且他们无法使用可能会生成重复项的现有方法很少像UUID。因此,他们设计了一种方法。

他们设置了一个自定义通用纪元,例如今天以长整数为基点。因此,有了它们,它有一个42位长的整数,从那个时期开始,在0+时间开始。

然后,如果一台计算机上的单个进程必须在同一毫秒内生成2个或更多ID,则他们还添加了一个12位长的整数序列。现在它们有42 + 12 = 54位在使用,当您考虑到多台机器上的多个进程(通常每个数据中心只有一台机器提供ID,但是可能更多,并且通常每台机器只有一个工作者/进程)时,您会意识到您不仅需要42 + 12。.

因此,他们还必须编码数据中心ID和“工人”(流程)ID。这将覆盖多个数据中心,每个数据中心中都有多个工作人员。这两个ID均为5位长的整数。所有这些整数都是无符号的,因此这5位整数可以增加到31,这使这些部分ID中的每一个都有32种可能性,包括0。因此,有32个数据中心,每个数据中心最多有32个工作线程。 42 + 12 + 5 + 5 = 64位,最多32x32 = 1024个工人分发这些ID。

所以..能够适应42位部分的使用寿命长达139年...节点ID(或数据中心+工人ID)的10位... 12位序列(4096每位员工每毫秒的ID)...您想出了64种最大保证的唯一ID系统/公式,在139年中,它的扩展性非常好,它不以任何方式依赖数据库,但可以高效地生成并存储在数据库中。数据库。

因此,此ID系统的计算结果为42 + 12 + 10,您可以将这10位除以或除以10位,但是您喜欢并且不希望超出在任何地方存储64位无符号长整数的范围。非常灵活,效果很好。

同样,它被称为雪花ID,Twitter提出了它。这10位可以称为分片ID,节点ID或数据中心ID和工作人员ID的组合,这实际上取决于您的需求。但是,通过不将分片/节点ID绑定到用户,而是将其绑定到多个进程,并能够在多个“事物”之间使用该ID,您将不必担心很多事情,并且可以跨多个包含很多事物的数据库和and and ..

一件重要的事情是,分片/节点ID只能容纳1024个不同的值,并且用户ID或他们可以使用的任何唯一ID不会从0分配到1023,因为它们没有分配它自己去做。

因此,您看到,这10位必须东西,它们是静态的,可分配的,并且易于解析,无论它们如何。

这是一个简单的python函数,它将生成雪花ID:

def genSnowflakeId(worker_id, data_center_id, ids_generated):
    "Returns a snowflake ID - This function will generate a unique ID that fits in a 64 bit unsigned number that scales for multiple workers running in mutiple datacenters. You must manage a timestamp and sequence sanity with ids_generated (i.e. increment if time apart < 1 millisecond or always increment and roll over to 0 if > 4095). Ultimately this will allow you to efficiently generate unique IDs across multiple locations for 139 years that fits in a bigint(20) database field and can be parsed for the created timestamp, worker ID, and datacenter ID. See https://github.com/twitter-archive/snowflake/tree/snowflake-2010"

    import sys
    import time

    # Mon Jul  8 05:07:56 EDT 2019
    twepoch = 1562576876131L

    sequence = 0L
    worker_id_bits = 5L
    data_center_id_bits = 5L
    sequence_bits = 12L
    timestamp_bits = 42L
    #total bits 64

    max_worker_id = -1L ^ (-1L << worker_id_bits)
    max_data_center_id = -1L ^ (-1L << data_center_id_bits)
    max_ids_generated = -1L ^ (-1L << sequence_bits)

    worker_id_shift = sequence_bits
    data_center_id_shift = sequence_bits + worker_id_bits
    timestamp_left_shift = sequence_bits + worker_id_bits + data_center_id_bits
    sequence_mask = -1L ^ (-1L << sequence_bits)


    # Sanity checks for input
    if worker_id > max_worker_id or worker_id < 0:
        raise ValueError("worker_id", "worker id can't be greater than %i or less than 0" % max_worker_id)
    if data_center_id > max_data_center_id or data_center_id < 0:
        raise ValueError("data_center_id", "data center id can't be greater than %i or less than 0" % max_data_center_id)
    if ids_generated > max_ids_generated or ids_generated < 0:
        raise ValueError("ids_generated", "ids generated can't be greater than %i or less than 0" % max_ids_generated)

    timestamp = long(int(time.time() * 1000))

    new_id = ((timestamp - twepoch) << timestamp_left_shift) | (data_center_id << data_center_id_shift) | (worker_id << worker_id_shift) | sequence

    return new_id

希望这个答案满足您:)

答案 1 :(得分:0)

他们需要一个长度为64位的图片ID。

从纪元以来的41毫秒(毫秒)+ shard-id的13位+递增值的10位。

他们之所以使用shard-id而不是user-id,是因为仅shard-id可以容纳13位,而user-id将需要更多位。