6

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

我在这里有什么误解?

4

2 回答 2

8

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

他们正在使用 Twitter Snowflake ID。这是为了在多个服务器、多个数据中心、并行生成唯一 ID。例如,在同一时刻,两个“地方”中的两个“项目”需要一个保证的唯一 ID,以便在同一时刻相隔不到一毫秒,甚至可能在同一纳秒内……这个唯一 ID 具有以下要求需要以极快的速度生成、高效、以可以有效解析的逻辑方式构建,可以容纳在 64 位内,并且生成它的方法需要能够处理大量的 ID,如果 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。

所以.. 42 位部分的寿命长达 139 年... 10 位用于节点 ID(或数据中心 + 工作人员 ID)... 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

希望这个答案能满足你:)

于 2019-07-25T06:58:54.873 回答
0

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

自纪元以来的毫秒数为 41 位 + 分片 ID 为 13 位 + 自动增量值为 10 位。

他们使用 shard-id 而不是 user-id 只是因为只有 shard-id 适合 13 位,而 user-id 需要更多位。

于 2019-07-24T21:58:48.943 回答