之前寫過一篇博客分析了一致性哈希,最近發現在Guava
中有一個Hashing
類簡單實現了一個一致性哈希的算法。
它使用起來非常簡單,里面有一個consistentHash()
的靜態方法:
int bucket = Hashing.consistentHash(id, buckets) // bucket 的范圍在 0 ~ buckets 之間
傳入數據主鍵id
(分片片鍵)和集群中機器數量buckets
,返回一個固定的數字,表示數據應當落在第幾個機器上。
而這個方法內部實現也非常簡單:
public static int consistentHash(long input, int buckets) {
// 檢查
checkArgument(buckets > 0, "buckets must be positive: %s", buckets);
// 利用內部的LCG算法實現,產生偽隨機數
LinearCongruentialGenerator generator = new LinearCongruentialGenerator(input);
int candidate = 0;
int next;
// Jump from bucket to bucket until we go out of range
while (true) {
// generator.nextDouble() 產生偽隨機數
// 每次hash的循環中每一個的next的值總是會固定 :
// 比如:
// hash 1 round 1 -> 9 hash 2 round 1 -> 9
// hash 1 round 2 -> 7 hash 2 round 2 -> 7
// hash 1 round 3 -> 2 hash 2 round 3 -> 2
next = (int) ((candidate + 1) / generator.nextDouble());
if (next >= 0 && next < buckets) {
// 如果在 0 到 bucket 范圍之外, 將這個next值賦值給candidate,重新計算
candidate = next;
} else {
// 如果在 0 到 bucket 范圍之內, 就返回這個 candidate 值,作為 input數據存儲的槽
return candidate;
}
}
}
// LCG偽隨機數的算法實現,關于LCG的解釋可以參考 http://en.wikipedia.org/wiki/Linear_congruential_generator
private static final class LinearCongruentialGenerator {
private long state;
public LinearCongruentialGenerator(long seed) {
this.state = seed;
}
public double nextDouble() {
state = 2862933555777941757L * state + 1;
return ((double) ((int) (state >>> 33) + 1)) / (0x1.0p31);
}
}
通過Guava
的這個方法,我們就可以輕松地在項目中使用一致性哈希了。