Kinesis分区键总是落在同一个分片中。

5

我是一名有用的助手,可以将文本翻译成中文。

我有一个包含2个分片的kinesis流,看起来像这样:

{
    "StreamDescription": {
        "StreamStatus": "ACTIVE",
        "StreamName": "my-stream",
        "Shards": [
            {
                "ShardId": "shardId-000000000001",
                "HashKeyRange": {
                    "EndingHashKey": "17014118346046923173168730371587",
                    "StartingHashKey": "0"
                },
            {
                "ShardId": "shardId-000000000002",
                "HashKeyRange": {
                    "EndingHashKey": "340282366920938463463374607431768211455",
                    "StartingHashKey": "17014118346046923173168730371588"
                },
        ]
    }
}

发送方设置一个分区,通常是UUID。它总是落在shard-002之上,这使得系统不具有负载平衡性,因此无法扩展。
顺便提一下,Kinesis使用md5sum分配记录,然后将其发送到包含其范围内的结果哈希的shard中。实际上,当我在使用的UUID上进行测试时,它们总是落在同一个shard中。
echo -n 80f6302fca1e48e590b09af84f3150d3 | md5sum
4527063413b015ade5c01d88595eec11  

17014118346046923173168730371588 < 4527063413b015ade5c01d88595eec11 < 340282366920938463463374607431768211455

有任何解决这个问题的想法吗?
2个回答

13

首先,请查看此问答:如何决定 AWS kinesis 流中的分区键总数?

关于您的情况,您有2个分片,但它们的哈希键范围不相等。

分片1包含的分区键数量:

17014118346046923173168730371587 - 0 = 17014118346046923173168730371587

分片2包含的分区键数量:

340282366920938463463374607431768211455 - 17014118346046923173168730371587 = 340282349906820117416451434263037839868

这两者之间存在很大差异;

17014118346046923173168730371587:17 x 10^30

340282349906820117416451434263037839868:34 x 10^37

如果分片1在“0-170141183460469231731687303715884105727”之间,分片2在“170141183460469231731687303715884105728-340282366920938463463374607431768211455”之间,那就太棒了。

您可能使用了桌面计算机或其他低精度计算器。请尝试更好的计算器。请参见下面的示例;

package com.cagricelebi.kinesis.core.utils;

import java.math.BigInteger;

public class MyCalc {

    public static void main(String[] args) {
        try {

            String num1 = "340282366920938463463374607431768211455";
            String num2 = "-17014118346046923173168730371587";

            String diff = bigCalc(num1, num2, "1", "1");
            System.out.println("result1 : " + diff); // 340282349906820117416451434263037839868

            String optimumHalf = bigCalc(num1, "0", "1", "2");
            System.out.println("result2 : " + optimumHalf); // 170141183460469231731687303715884105727

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Basic calculator.
     * First adds up first two elements, than multiplies the summation.
     * The result is the division of the multilication to divisor.
     *
     * @param bigInt A
     * @param bigInt2 B
     * @param multiplicator C
     * @param divisor D
     * @return ((A+B)*C)/D
     */
    private static String bigCalc(String bigInt, String bigInt2, String multiplicator, String divisor) {
        BigInteger summation = new BigInteger(bigInt).add(new BigInteger(bigInt2));
        BigInteger multiplication = summation.multiply(new BigInteger(multiplicator));
        BigInteger division = multiplication.divide(new BigInteger(divisor));
        return division.toString();
    }

}

没错,这正是我上面描述的问题。看起来你在我的回复上传之前就回答了这个问题。很高兴你找到了答案,点赞!!! - isaac.hazan

4
经过几个小时的调查,我找到了根本原因,又是人为错误。即使这很简单,我还是在这里分享解决方案,以节省其他人可能花费的时间。
问题出现的原因是原始流的分割方式。当您使用一个分片来分割流时,必须计算新子分片的起始哈希键。这个新的哈希键通常在父分片哈希键范围的中间。
新创建的分片(父分片)将具有以下范围:
0 - 340282366920938463463374607431768211455

所以你天真地去使用Windows计算器,复制并粘贴这个数字“340282366920938463463374607431768211455”,然后除以2。

我错过的问题,也很容易被忽略的是,Windows计算器实际上会截断数字而不告诉你。上面的数字在计算器中粘贴后将变为“34028236692093846346337460743176”。一旦你除以2,你得到的数字实际上非常小,与父分片的范围相比,然后你的记录将不会被分布,它们将进入获得更大范围的分片。

一旦你把上面的数字带到适用于大数的计算器中,你就会得到范围的中间值。 我用这个来计算范围:https://defuse.ca/big-number-calculator.htm

经过这个改变,记录被完美地分布,系统可以很好地扩展。


md5哈希与数字分区范围相比如何?假设我有paritionKey="1",其md5哈希值为c4ca4238a0b923820dcc509a6f75849b。它属于第1个还是第2个分区? - prayagupa
似乎对于分区键1,数据将被路由到shard1(261578874264819908609102035485573088411 -> shardId-000000000001),而对于分区键96,数据将被路由到shard0(51037628727478963551185524537127703940 -> shardId-000000000000)。 - prayagupa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接