假设我有6位数的订单ID:
000000
000001
000003
...
000020
...
999999
并假设每个都来自分布式系统中的不同节点,我想将节点id编码为顺序。最简单的方法是简单地保留节点ID的前2位数,如下所示:
010000 - second node
020001 - third node
010003 - second node again
150004 - 16th node
...
这样做很好,但是因为我肯定知道我只期待少量节点(比方说16)我失去了很多可能的id,将自己限制在10 ^ 4而不是10 ^ 6。是否有一种智能方法可以编码15个唯一节点而不限制可能的数字?理想情况下,我会有10^6 - 15
的可能性。
编辑:我正在寻找一个解决方案,不会平均分配范围到每个节点ID。我正在寻找一种方法来编码已经存在的唯一ID中的节点id,而不会(理想情况下)丢失可能性节点的数量。
EDIT2:这必须是6位数的字符串表示的原因是因为我正在使用的API需要这个。不幸的是,没有办法绕过它。
我失去了许多可能的id,限制自己基本上10 ^ 4而不是10 ^ 6。
我们总共还有10^4 * 16
ids。
是否有一种智能方法可以编码15个唯一节点而不限制可能的数字?
此问题类似于distributed hash table密钥空间分区。解决该问题的最着名的解决方案是创建大量虚拟节点,在这些虚拟节点之间划分密钥空间,然后以特定方式将这些虚拟节点分配给物理(循环,随机,按需等)。
实现密钥空间分区的最简单方法是确保每个节点生成这样一个id:
vnode_id = order_id % total_number_of_vnodes
例如,如果我们只有3个vnodes [0,1,2],那么:
vnode 0 must generate ids: 0, 3, 6, 9...
vnode 1 must generate ids: 1, 4, 7, 10...
vnode 2 must generate ids: 2, 5, 7, 11...
如果我们有7个vnodes [0,1,2,3,4,5,6],那么:
vnode 0 must generate ids: 0, 7, 14, 21...
vnode 1 must generate ids: 1, 8, 15, 22...
vnode 2 must generate ids: 2, 9, 16, 23...
...
vnode 6 must generate ids: 6, 13, 20, 27...
然后,所有物理节点必须以已知和常见的方式映射到虚拟,例如1:1映射:
physical node 0 takes vnode 0
physical node 1 takes vnode 1
physical node 2 takes vnode 2
按需映射:
physical node 0 takes vnode 0, 3, 7 (many orders)
physical node 1 takes vnode 1, 4 (less orders)
physical node 2 takes vnode 2 (no orders)
我希望你能理解这个想法。
理想情况下,我会有10 ^ 6-15种可能性。
不幸的是,这是不可能的。考虑一下:我们有10 ^ 6个可能的id和15个不同的节点,每个节点都生成一个唯一的id。
基本上,这意味着我们在节点之间划分我们的id,即每个节点获得平均10^6 / 15
,这远远小于理想的10^6 - 15
。
使用上述方法,我们仍然总共有10^6
id,但它们将在vnode之间进行分区,而vnode又将映射到物理节点。这是您的问题AFAIK的最佳实用解决方案。
我正在寻找一种不会将范围平均分配给每个节点id的解决方案。我正在寻找一种方法来编码已经存在的唯一ID中的节点id,而不会(理想情况下)丢失可能性节点的数量。
不要指望奇迹。可能还有很多值得尝试的其他技巧。
例如,如果服务器和所有客户端都知道下一个订单ID必须是235,但是说客户端5生成订单ID 240(235 + 5)并将其发送到服务器。
服务器期望订单ID 235,但是接收订单ID 240.所以现在服务器知道该订单来自客户端5(240-235)。
或者我们可以尝试使用另一个字段来存储客户端ID。例如,如果您有时间提交(HH:MM.SS),我们可能会使用秒来存储客户端ID。
只是一些例子,我想你明白了......
设n
是6位输入的前2位数字。假设您有16个节点,我们可以这样做:
nodeId = n % 16
也:
highDigit = n / 16
其中/
表示整数除法。对于16个节点,highDigit = [0..6]
如果m
是输入的最后4位数字代表的数字,那么我们可以通过以下方式恢复原始订单ID:
orderId = highDigit*10^5 + m
使用此方案和16个节点,您可以表示6 * 10 ^ 5 + 10 ^ 4个订单ID。
您可以将10 ^ 6个可能的ID拆分为接近相等的块,其中每个块的起始索引等于10 ^ 6除以块的数量,向下舍入,乘以块索引,块大小为10 ^ 6除以块数,向下舍入。在您的示例中有十六个块:
10^6 / 16 = 62,500
chunk1: [ 0, 62500)
chunk2: [ 62500, 125000)
chunk3: [125000, 187500)
chunk4: [187500, 250000)
chunk5: [250000, 312500)
chunk6: [312500, 375000)
chunk7: [375000, 437500)
chunk8: [437500, 500000)
chunk9: [500000, 562500)
chunk10: [562500, 625000)
chunk11: [625000, 687500)
chunk12: [687500, 750000)
chunk13: [750000, 812500)
chunk14: [812500, 875000)
chunk15: [875000, 937500)
chunk16: [937500, 1000000)
要从节点X上的本地ID计算全局ID,请计算62500 * X +本地ID。要从节点确定节点和本地ID,请计算node = global ID / 62500 round down和local ID = global ID mod 62500。
这样做基本上可以使用所有可用的索引,直到舍入误差。与节点之间的I / O相比,整数上的除法和模数应该相对较快。
由于您已选择使用数字(而不是位,我们可以将整个练习压缩为32位数字),这是编码节点ID的一种方法。也许其他人可以想出更多的想法。
将数字字母扩展到J
。想象一下,节点ID的位分布在六位数上。对于每个设置位,将订单ID的十进制数字映射到一个字母:
0 -> A
1 -> B
2 -> C
...
9 -> J
例如:
{759243, 5} -> 759C4D
现在,您可以将所有10 ^ 6个订单ID与6位节点ID一起编码。