更好的分片功能int64键在golang?



我使用这个repo的并发映射,在使用NewWithCustomShardingFunction创建映射时可以选择键类型。我只需要为int64密钥提供我自己的分片功能,这就是我在这里使用的。

我也使用最新版本的Go,在那里我可以使用泛型,所以我决定使用concurrent-map与密钥作为int64通过实现我自己的分片功能。

import (
cmap "github.com/orcaman/concurrent-map/v2"
)
func shardingFunc(key int64) uint32 {
return uint32(key) // TODO - create a better sharding function that does not rely on how uint32 type conversion works
}
func main() {
testMap := cmap.NewWithCustomShardingFunction[int64, *definitions.CustomerProduct](shardingFunc)
// ... use the map ...
}

我想知道我的分片功能是否可以在这里为int64键,或者我应该有更好的分片功能?我不想在这种情况下,它可以给我index out of range错误或任何其他问题。

分片函数是一个哈希函数。该函数应该在32位空间内均匀地分发密钥。

如果低四个字节如果你的init64值是均匀分布的,那么uint32(key)将作为一个分片函数。

uint32(key)是一个不好的选择的例子是低字节具有常量值。例如,如果键值为0x00010000, 0x00020000,…,则uint32(key)的值为0。这不是一个均匀分布。

如果你不知道你的int64密钥是如何分布的,那么最好在shard函数中使用所有密钥的位。下面是一个使用异或的例子:

func shardingFunc(key int64) uint32 {
return uint32(key) ^ uint32(key >> 32) 
}

对于一些健壮的东西,使用crypto/sha256对密钥进行哈希,然后将其(部分)转换为unint32:

func shardingFunc(key int64) uint32 {
bytes := sha256.Sum256(new(big.Int).SetInt64(key).Bytes())
return binary.BigEndian.Uint32(bytes[0:32])
}

有更有效的编码方式,尽管更冗长,但希望你能理解。

这是一个更加上下文敏感的答案,因为我通过聊天了解了更多的背景信息。

首先,你不会得到一个超出范围的索引。因为并发映射库总是在除以分片数后取余数。

其次,整数产品id通常是顺序的,这意味着它们将自然地均匀分布在每个分片上。

如果你碰巧有病态对立的更新/访问模式,可能会有一些例外,但实际上这没有什么区别。即使在只使用1个shard的最坏情况下,性能也会有效地与使用常规Go映射相同,因为每个shard内部都有自己的常规映射。如果您发现自己确实处于性能很重要的情况下,您最好滚动您自己的并发映射,而不是使用这个库。我知道它被标榜为一个"高性能的解决方案",但是没有所谓的"一刀切的优化"。这是一个矛盾修饰法。

最新更新