使用后缀或前缀以紧凑的方式编码数字

问题描述 投票:2回答:4

假设我有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需要这个。不幸的是,没有办法绕过它。

algorithm language-agnostic uniqueidentifier
4个回答
1
投票

我失去了许多可能的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。

只是一些例子,我想你明白了......


0
投票

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。


0
投票

您可以将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相比,整数上的除法和模数应该相对较快。


0
投票

由于您已选择使用数字(而不是位,我们可以将整个练习压缩为32位数字),这是编码节点ID的一种方法。也许其他人可以想出更多的想法。

将数字字母扩展到J。想象一下,节点ID的位分布在六位数上。对于每个设置位,将订单ID的十进制数字映射到一个字母:

0 -> A
1 -> B
2 -> C
...
9 -> J

例如:

{759243, 5} -> 759C4D

现在,您可以将所有10 ^ 6个订单ID与6位节点ID一起编码。

© www.soinside.com 2019 - 2024. All rights reserved.