mysql分布式一致性hash_分布式哈希一致性
問題
分布式哈希一致性的動機是什么?
相比其他有什么好處
概述
我們談論的分布式哈希一致性常常使用在負載均衡,權衡一個策略的好壞,我們常常談到擴展性和容錯性。我們可以從以下兩個方面來考量
擴展性 :水平擴展和垂直擴展,加減一臺cluster 是否對整個集群有影響。
容錯性 :假如一臺cluster 是否會影響到其他的 cluster,是否可以用比較小的代價進行恢復。
負載均衡
負載均衡使用的策略 :
隨機訪問策略。系統隨機訪問,服務器負載壓力不均衡,所以有可能分配的不合理。
輪詢策略。請求均勻分配,如果服務器有性能差異,則無法實現性能好的服務器能夠多承擔一部分。
權重輪詢策略。權值需要靜態配置,無法自動調節,不適合對長連接和命中率有要求的場景。
Hash取模策略。不穩定,如果列表中某臺服務器宕機,則會導致路由算法產生變化,由此導致命中率的急劇下降。 一致性哈希策略。
Hash取模策略
hash取模可以定義為 “ key % n = 目標cluster ”,key可以相當于 java 中的hashcode ,n 則為當前所有集群機器的數量。 但是它的缺點很明顯,假如使用hash取模策略,若增加臺cluster,(圖片來自參考資料)
減少臺機器
可以看到增加臺機器,會導致key 重新映射,那么遷移工作將是巨大的,而減少一臺也是同樣的道理,最重要的原因是該策略是依賴機器的數量進行分配目標機器。
分布式一致性hash算法
直接上圖(來源見參考資料)
上面是hash環,cluster 分布在hash環上,請求經過hash 等到的key沿著順時針,第一個到達的節點就是目標cluster .
擴展性和容錯性
假如增加/減少了一臺機器,對其他的cluster并沒有什么影響,而某臺cluster發生故障后,恢復的時候只需要將故障節點的keys 放在沿著hash環下一臺節點就可以了。
虛擬節點
但是我們將節點分布在hash 環上的時候有可能分布不均,例如
上圖,服務器1接受的請求在平等條件下肯定比其他的多,我們可以通過增設虛擬節點的方式來解決這個問題,虛擬節點并不是真實的物理節點,是虛構出來的,這樣可以解決節點在hash 環中不平衡的問題,同時也是根據權重不同可以分配多幾個虛擬節點。
運用
rocketmq 集群消費端 和 dubbo (RPC) 對服務提供者負載均衡的時候會使用
MySQL 分庫分表的實現也是可以使用一致性Hash 算法 (這個在后續的文章中會提到)
這里的dubbo中的一致性hash 算法的運用和 數據庫的運用有點不同,為什么呢? dubbo 是 RPC ,某個節點down 或是增加了節點,并沒有影響,而數據庫就不同了,數據庫存儲的是數據,某個節點down 了數據就找不到了,后續文章再詳細說明。
具體實現
我們看一下 dubbo 中關于hash 一致性算法的實現,,主要的實現是依靠? TreeMap 的 ceilingEntry? 方法, 該方法的注解 :
The ceilingEntry(K key) method is used to return a key-value mapping associated with the least key greater than or equal to the given key, or null if there is no such key.
就是返回最相近的值,這和 一致性 hash 算法的key 繞著圈走到最近的節點思路一致。 dubbo 中的一致性hash實現在 ConsistentHashLoadBalance 類,我們直接看 doSelect 方法,其中 invokers 就是候選的節點,invocation 是調用者的封裝,最后返回的 Invoker 自然就是目標 invoker .
1 public class ConsistentHashLoadBalance extendsAbstractLoadBalance {2 public static final String NAME = "consistenthash";3
4 /**
5 * Hash nodes name6 */
7 public static final String HASH_NODES = "hash.nodes";8
9 /**
10 * Hash arguments name11 */
12 public static final String HASH_ARGUMENTS = "hash.arguments";13
14 private final ConcurrentMap> selectors = new ConcurrentHashMap>();15
16 @SuppressWarnings("unchecked")17 @Override18 protected Invoker doSelect(List>invokers, URL url, Invocation invocation) {19 String methodName =RpcUtils.getMethodName(invocation);20 String key = invokers.get(0).getUrl().getServiceKey() + "." +methodName;21 //using the hashcode of list to compute the hash only pay attention to the elements in the list
22 int invokersHashCode =invokers.hashCode();23 ConsistentHashSelector selector = (ConsistentHashSelector) selectors.get(key);24 if (selector == null || selector.identityHashCode !=invokersHashCode) {25 selectors.put(key, new ConsistentHashSelector(invokers, methodName, invokersHashCode));26 selector = (ConsistentHashSelector) selectors.get(key);27 }28 //該給 ConsistentHashSelector 方法
29 returnselector.select(invocation);30 }31
32 private static final class ConsistentHashSelector{33
34 private final TreeMap>virtualInvokers;35
36 private final intreplicaNumber;37
38 private final intidentityHashCode;39
40 private final int[] argumentIndex;41
42 ConsistentHashSelector(List> invokers, String methodName, intidentityHashCode) {43 this.virtualInvokers = new TreeMap>();44 this.identityHashCode =identityHashCode;45 URL url = invokers.get(0).getUrl();46 this.replicaNumber = url.getMethodParameter(methodName, HASH_NODES, 160);47 String[] index = COMMA_SPLIT_PATTERN.split(url.getMethodParameter(methodName, HASH_ARGUMENTS, "0"));48 argumentIndex = new int[index.length];49 for (int i = 0; i < index.length; i++) {50 argumentIndex[i] =Integer.parseInt(index[i]);51 }52 //replicaNumber就是復制的節點,默認是 160 ,假如invokers 的數量是 5 ,那么總的節點數就是 5*160
53 for (Invokerinvoker : invokers) {54 String address =invoker.getUrl().getAddress();55 for (int i = 0; i < replicaNumber / 4; i++) {56 byte[] digest = md5(address +i);57 for (int h = 0; h < 4; h++) {58 long m =hash(digest, h);59 //virtualInvokers 是個 TreeMap
60 virtualInvokers.put(m, invoker);61 }62 }63 }64 }65
66 public Invokerselect(Invocation invocation) {67 String key =toKey(invocation.getArguments());68 byte[] digest =md5(key);69 return selectForKey(hash(digest, 0));70 }71
72 privateString toKey(Object[] args) {73 StringBuilder buf = newStringBuilder();74 for (inti : argumentIndex) {75 if (i >= 0 && i
82 private Invoker selectForKey(longhash) {83 //看這里!!調用 ceilingEntry 方法
84 Map.Entry> entry =virtualInvokers.ceilingEntry(hash);85 if (entry == null) {86 entry =virtualInvokers.firstEntry();87 }88 returnentry.getValue();89 }90
91 private long hash(byte[] digest, intnumber) {92 return (((long) (digest[3 + number * 4] & 0xFF) << 24)93 | ((long) (digest[2 + number * 4] & 0xFF) << 16)94 | ((long) (digest[1 + number * 4] & 0xFF) << 8)95 | (digest[number * 4] & 0xFF))96 & 0xFFFFFFFFL;97 }98
99 private byte[] md5(String value) {100 MessageDigest md5;101 try{102 md5 = MessageDigest.getInstance("MD5");103 } catch(NoSuchAlgorithmException e) {104 throw newIllegalStateException(e.getMessage(), e);105 }106 md5.reset();107 byte[] bytes =value.getBytes(StandardCharsets.UTF_8);108 md5.update(bytes);109 returnmd5.digest();110 }111
112 }113
114 }
上面使用了hash 算法 ,在和?https://github.com/RJ/ketama?該地址下看到的有點相似,這里我們只需要知道hash 的作用是使得使值均勻分布。
總結
通過本文了解了分布式哈希一致性相對與其他的負載均衡策略的優勢。
參考資料
https://www.acodersjourney.com/system-design-interview-consistent-hashing/
https://www.cnblogs.com/jajian/p/10896624.html
總結
以上是生活随笔為你收集整理的mysql分布式一致性hash_分布式哈希一致性的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 服务器mdl文件转换,Simulink
- 下一篇: mysql一个表几亿数据_如何在mysq