Service Fabric - 我们如何生成 partitionKey?
Service Fabric - How could we generate a partitionKey?
我有一个有状态服务,其中有一系列分区键来自
-9223372036854775808 到 9223372036854775807(UniformInt64Partition)。
如何在调用服务时生成足够的分区键以改善所有分区之间的工作负载分布?
谢谢你
对于这种大范围的分区键,最好的方法是在字段或字段集合之上使用 hashing algorithm 来生成尽可能少冲突的键(数字)。
假设您正在存储客户信息,例如,来自 "John Smith" 的客户名称的散列可以生成散列值 32,因为任何与 "John Smith" 同名的用户都会生成相同的散列,如果不频繁,不会有问题,因为 32 不是 id,它们可以重复,具有相同的散列,它们将存储在同一个分区上。
如果你真的想尽可能均匀地分配这些值,你可以使用另一个连接的字段来区分 "John Smith" 和 "John Smith",比如出生日期,除非两者出生在同一日期,你会发现每一个都有不同的值。
在你的情况下,因为范围非常大,你必须使用哈希算法对这些值进行哈希处理,以适应 -9223372036854775808 到 9223372036854775807 的范围。
需要那么多钥匙吗?
如果您的系统不希望有很多分区,管理它的一种简单方法是使用一个自然数,该自然数密切反映您选择的散列函数提供的键范围,您可能会决定选择一个具有更好的性能,或更低的碰撞,或两者兼而有之。
如果您已经使用 GUID 作为识别数据的密钥,这并不难做到。要知道的关键是,GUID 虽然(实际上)在全球范围内是唯一的,但在一个范围内甚至没有接近均匀分布。我使用 SHA1 散列算法对 GUID 进行散列,因为尽管它 shortcomings as a cryptographic algorithm,但它在生成均匀分布的散列方面做得很好,而不需要太多的服务器(在计算和 RAM 方面)。
附带说明一下,从 GUID 变为长整数会导致数据丢失(GUID 相当于 128 位整数)。由于目标是跨分区分布数据,所以这没关系……不要为小事操心。事实上,您可以使用比 Int64 更小的范围,但如果您已经有了 GUID,那又何必呢。
请参阅之前的代码,了解从 GUID 创建分区键的扩展方法。我的实现代码将它折叠成两行,但我将其分解在下面以便我可以对其进行注释。
public static ServicePartitionKey ToPartitionKey(this Guid id)
{
// Hash algorithms need byte arrays, so we're converting the Guid here
byte[] guidBytes = id.ToByteArray();
// SHA1 is light weight and good at creating distribution across the range.
// Do not use for encryption!
SHA1CryptoServiceProvider hasher = new SHA1CryptoServiceProvider();
// Hash the Guid's bytes.
byte[] hashedBytes = hasher.ComputeHash(guidBytes);
// Now that our data is repeatibly but distributed evenly, we make it a long
long guidAsLong = BitConverter.ToInt64(hashedBytes, 0);
// return the partition key
return new ServicePartitionKey(guidAsLong);
}
我有一个有状态服务,其中有一系列分区键来自
-9223372036854775808 到 9223372036854775807(UniformInt64Partition)。
如何在调用服务时生成足够的分区键以改善所有分区之间的工作负载分布?
谢谢你
对于这种大范围的分区键,最好的方法是在字段或字段集合之上使用 hashing algorithm 来生成尽可能少冲突的键(数字)。
假设您正在存储客户信息,例如,来自 "John Smith" 的客户名称的散列可以生成散列值 32,因为任何与 "John Smith" 同名的用户都会生成相同的散列,如果不频繁,不会有问题,因为 32 不是 id,它们可以重复,具有相同的散列,它们将存储在同一个分区上。
如果你真的想尽可能均匀地分配这些值,你可以使用另一个连接的字段来区分 "John Smith" 和 "John Smith",比如出生日期,除非两者出生在同一日期,你会发现每一个都有不同的值。
在你的情况下,因为范围非常大,你必须使用哈希算法对这些值进行哈希处理,以适应 -9223372036854775808 到 9223372036854775807 的范围。
需要那么多钥匙吗?
如果您的系统不希望有很多分区,管理它的一种简单方法是使用一个自然数,该自然数密切反映您选择的散列函数提供的键范围,您可能会决定选择一个具有更好的性能,或更低的碰撞,或两者兼而有之。
如果您已经使用 GUID 作为识别数据的密钥,这并不难做到。要知道的关键是,GUID 虽然(实际上)在全球范围内是唯一的,但在一个范围内甚至没有接近均匀分布。我使用 SHA1 散列算法对 GUID 进行散列,因为尽管它 shortcomings as a cryptographic algorithm,但它在生成均匀分布的散列方面做得很好,而不需要太多的服务器(在计算和 RAM 方面)。
附带说明一下,从 GUID 变为长整数会导致数据丢失(GUID 相当于 128 位整数)。由于目标是跨分区分布数据,所以这没关系……不要为小事操心。事实上,您可以使用比 Int64 更小的范围,但如果您已经有了 GUID,那又何必呢。
请参阅之前的代码,了解从 GUID 创建分区键的扩展方法。我的实现代码将它折叠成两行,但我将其分解在下面以便我可以对其进行注释。
public static ServicePartitionKey ToPartitionKey(this Guid id)
{
// Hash algorithms need byte arrays, so we're converting the Guid here
byte[] guidBytes = id.ToByteArray();
// SHA1 is light weight and good at creating distribution across the range.
// Do not use for encryption!
SHA1CryptoServiceProvider hasher = new SHA1CryptoServiceProvider();
// Hash the Guid's bytes.
byte[] hashedBytes = hasher.ComputeHash(guidBytes);
// Now that our data is repeatibly but distributed evenly, we make it a long
long guidAsLong = BitConverter.ToInt64(hashedBytes, 0);
// return the partition key
return new ServicePartitionKey(guidAsLong);
}