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

use*_*636 6 database facebook sharding id-generation instagram

我正在阅读: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功能所属的分片!

我在这里误会什么?

小智 7

也许我可以为您更好地分解它,这不仅仅是因为用户 ID 不适合。

他们正在使用 Twitter Snowflake ID。这旨在并行生成跨多个服务器、跨多个数据中心的唯一 ID。例如,在同一时刻,两个“位置”中的两个“项目”需要一个有保证的唯一 ID,用于同一时间间隔小于一毫秒的任何东西,甚至可能在同一纳秒......这个唯一 ID 有以下要求需要非常快速地生成、高效、以可以有效解析的逻辑方式构建、可以适应 64 位,并且生成它的方法需要能够处理大量的 ID,如果 ID 超过许多人的生命周期。这意味着他们无法通过数据库查找来获取尚未使用的唯一 ID,也无法确定生成的 ID 在生成后是唯一的,并且他们不能' t 使用可能生成重复项的现有方法,即使很少像 UUID。于是他们想了个办法..

他们设置了一个自定义的公共纪元,例如今天以一个长整数作为基点。所以有了这个,他们有一个 42 位长的整数,从那个时代起从 0+时间开始。

然后他们还添加了一个 12 位长整数的序列,以防单台机器上的单个进程必须在同一毫秒内生成 2 个或更多 ID。现在他们有 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。

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

所以,这个 ID 系统的结果是 42+12+10,你可以将这 10 位分开,或者不分开,但是你喜欢并且不会超出在任何地方存储 64 位无符号长整数。非常灵活,效果很好。

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

重要的一件事是,该分片/节点 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
Run Code Online (Sandbox Code Playgroud)

希望这个答案让你满意:)