Kinesis 分区键始终位于同一个分片中
Kinesis partition key falls always in the same shard
我有一个带有 2 个分片的运动流,如下所示:
{
"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 分配记录,然后将其发送到其范围内包含结果哈希的分片。事实上,当我在我使用的 UUId 上测试它时,它们确实总是落在同一个碎片中。
echo -n 80f6302fca1e48e590b09af84f3150d3 | md5sum
4527063413b015ade5c01d88595eec11
17014118346046923173168730371588 < 4527063413b015ade5c01d88595eec11 < 340282366920938463463374607431768211455
知道如何解决这个问题吗?
经过几个小时的调查,我找到了根本原因,又是人为错误。在这里分享解决方案,即使它很简单,可以节省其他人可以花在上面的时间。
问题是由于原始流的拆分方式引起的。当您用一个分片拆分一个流时,您必须计算新子分片的起始哈希键。这个新的哈希键通常在父分片哈希键范围的中间。
新创建的分片(父分片)将具有以下范围:
0 - 340282366920938463463374607431768211455
所以你天真地去你的 Windows 计算器并复制粘贴这个“340282366920938463463374607431768211455”然后除以 2。
我遗漏并且很容易遗漏的问题是 Windows 计算器实际上会在不让您知道的情况下截断数字。粘贴在计算器中的上述数字现在将是“34028236692093846346337460743176”。一旦你将它除以 2,你实际上会得到一个与父分片的范围相比非常小的数字,然后你的记录将不会被分发,它们将转到范围较大部分的分片。
一旦您将上面的数字用于适用于大数字的计算器,您就会得到范围的中间值。
我用它来计算范围:https://defuse.ca/big-number-calculator.htm .
此更改后,记录完美分布,系统扩展性很好。
首先看这个问答:How to decide total number of partition keys in AWS kinesis stream?
关于你的情况;你有 2 个分片,但它们的哈希键范围不相等。
分片 1 包含的分区键数:
17014118346046923173168730371587 - 0 = 17014118346046923173168730371587
分片 2 包含的分区键数:
340282366920938463463374607431768211455 - 17014118346046923173168730371587 = 340282349906820117416451434263037839868
两者有很大区别;
17014118346046923173168730371587 : 17 x 10^30
340282349906820117416451434263037839868 : 34 x 10^37
如果shard 1在“ 0-170141183460460469231731731731687373715884105727”之间,那将是很棒的
您可能使用过台式机或其他精度较低的计算器。试试更好的计算器。请参阅下面的示例;
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();
}
}
我有一个带有 2 个分片的运动流,如下所示:
{
"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 分配记录,然后将其发送到其范围内包含结果哈希的分片。事实上,当我在我使用的 UUId 上测试它时,它们确实总是落在同一个碎片中。
echo -n 80f6302fca1e48e590b09af84f3150d3 | md5sum
4527063413b015ade5c01d88595eec11
17014118346046923173168730371588 < 4527063413b015ade5c01d88595eec11 < 340282366920938463463374607431768211455
知道如何解决这个问题吗?
经过几个小时的调查,我找到了根本原因,又是人为错误。在这里分享解决方案,即使它很简单,可以节省其他人可以花在上面的时间。
问题是由于原始流的拆分方式引起的。当您用一个分片拆分一个流时,您必须计算新子分片的起始哈希键。这个新的哈希键通常在父分片哈希键范围的中间。
新创建的分片(父分片)将具有以下范围:
0 - 340282366920938463463374607431768211455
所以你天真地去你的 Windows 计算器并复制粘贴这个“340282366920938463463374607431768211455”然后除以 2。
我遗漏并且很容易遗漏的问题是 Windows 计算器实际上会在不让您知道的情况下截断数字。粘贴在计算器中的上述数字现在将是“34028236692093846346337460743176”。一旦你将它除以 2,你实际上会得到一个与父分片的范围相比非常小的数字,然后你的记录将不会被分发,它们将转到范围较大部分的分片。
一旦您将上面的数字用于适用于大数字的计算器,您就会得到范围的中间值。 我用它来计算范围:https://defuse.ca/big-number-calculator.htm .
此更改后,记录完美分布,系统扩展性很好。
首先看这个问答:How to decide total number of partition keys in AWS kinesis stream?
关于你的情况;你有 2 个分片,但它们的哈希键范围不相等。
分片 1 包含的分区键数:
17014118346046923173168730371587 - 0 = 17014118346046923173168730371587
分片 2 包含的分区键数:
340282366920938463463374607431768211455 - 17014118346046923173168730371587 = 340282349906820117416451434263037839868
两者有很大区别;
17014118346046923173168730371587 : 17 x 10^30
340282349906820117416451434263037839868 : 34 x 10^37
如果shard 1在“ 0-170141183460460469231731731731687373715884105727”之间,那将是很棒的
您可能使用过台式机或其他精度较低的计算器。试试更好的计算器。请参阅下面的示例;
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();
}
}