又是一個夜黑風高的晚上,帶上無線耳機聽一曲。忽然很感慨一句話:生活就像心電圖,一路順風就證實你掛了。
就如同咱們幹運維的,以爲很簡單的事情,有時候能幹出無限可能
。仍是言歸正傳吧,這一次咱們來講說stringhash分區算法。java
1.hash分區算法
2.stringhash分區算法
3.enum分區算法
4.numberrange分區算法
5.patternrange分區算法
6.date分區算法
7.jumpstringhash算法mysql
<tableRule name="rule_hashString"> <rule> <columns>name</columns> <algorithm>func_hashString</algorithm> </rule> </tableRule> <function name="func_hashString" class="StringHash"> <property name="partitionCount">3,2</property> <property name="partitionLength">3,4</property> <property name="hashSlice">0:3</property> </function>
和以前的hash算法同樣。須要在rule.xml中配置tableRule和function。算法
1.partitionCount:指定分區的區間數,具體爲 C1 +C2 + ... + Cn
2.partitionLength:指定各區間長度,具體區間劃分爲 [0, L1), [L1, 2L1), ..., [(C1-1)L1, C1L1), [C1L1, C1L1+L2), [C1L1+L2, C1L1+2L2), ... 其中,每個區間對應一個數據節點。
3.hashSlice:指定參與hash值計算的key的子串。字符串從0開始索引計數sql
接下來咱們來詳細介紹一下StringHash的工做原理。咱們以上面的配置爲例。數據庫
字符串截取的範圍是hashSlice[0]到hashSlice[1]。好比我這裏設置0,3。‘buddy'這個字符串就會截取出bud
,相似數據庫中的substring
函數。數組
/** * String hash:s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1] <br> * h = 31*h + s.charAt(i); => h = (h << 5) - h + s.charAt(i); <br> * * @param start hash for s.substring(start, end) * @param end hash for s.substring(start, end) */ public static long hash(String s, int start, int end) { if (start < 0) { start = 0; } if (end > s.length()) { end = s.length(); } long h = 0; for (int i = start; i < end; ++i) { h = (h << 5) - h + s.charAt(i); } return h; }
這段源代碼的意思其實上面有解釋。算法是s[0]*31^(n-1) + s[1]31^(n-2) + ... + s[n-1]。而後接下來它說明h = 31h + s.charAt(i)是等同於h = (h << 5) - h + s.charAt(i)。你們是否是仍是雲裏霧裏的。你能夠去看文章結尾關於這一點的詳細解釋。less
這裏咱們把這個公式分解一下,根據上述的公式,咱們能推導出下列算術式:
i=0 -> h = 31 * 0 + s.charAt(0)
i=1 -> h = 31 * (31 * 0 + s.charAt(0)) + s.charAt(1)
i=2 -> h = 31 * (31 * (31 * 0 + s.charAt(0)) + s.charAt(1)) + s.charAt(2)
i=3 -> h = 31 * (31 * (31 * (31 * 0 + s.charAt(0)) + s.charAt(1)) + s.charAt(2)) + s.charAt(3)
.......以此內推運維
假設咱們的字符串是"buddy",咱們截取0-3字符串,咱們來算一下。根據上面的函數來寫段java代碼編譯運行。函數
public class test { public static void main(String args[]) { String Str = new String("buddy"); System.out.println(hash(Str,0,3)); } public static long hash(String s, int start, int end) { if (start < 0) { start = 0; } if (end > s.length()) { end = s.length(); } long h = 0; for (int i = start; i < end; ++i) { h = (h << 5) - h + s.charAt(i); } return h; } } [root@mysql5 java]# javac test.java [root@mysql5 java]# java test 97905
經過運行程序截取字符串buddy,0-3獲得的結果是97905。那麼這個結果是怎麼算出來的。首先截取0,3,最終截取的是三個字符串bud
。索引從0開始計數對應的就是i=2。根據i=2的公式:
i=2 -> h = 31 * (31 * (31 * 0 + s.charAt(0)) + s.charAt(1)) + s.charAt(2)
咱們能夠查詢ascii表
s.charAt(0),是算"b"這個字母的ASCII值,十進制數字爲98
s.charAt(1),是算"u"這個字母的ASCII值,十進制數字爲117
s.charAt(1),是算"d"這個字母的ASCII值,十進制數字爲100
性能
把上述三個值帶入到公式得出 31 * (31 * (31 * 0 + 98) + 117) + 100 = 97905。正好和咱們程序計算的值同樣。
97905 mod 17 =2 根據取模的值,落在了dn1分區,dn1分區是存放(0,3)的。
如圖所示,當咱們執行插入name='buddy',而後再一次查詢的name='buddy'的時候,直接路由到了第一個分區。和咱們以前計算的結果一致。
今天介紹的stringhash和hash分區算法大體相同,只不過對於字符串需先計算出hash值。該算法有個經典的數字叫31。這個數字大有來頭。《Effective Java》中的一段話說明了爲何要用31,由於31是一個奇質數,若是選擇一個偶數的話,乘法溢出信息將丟失。由於乘2等於移位運算。使用質數的優點不太明顯,但這是一個傳統。31的一個很好的特性是乘法能夠用移位和減法來代替以得到更好的性能:31*i==(i<<5)-i
。現代的 Java 虛擬機能夠自動的完成這個優化。
The value 31 was chosen because it is an odd prime. If it were even and the multiplication overflowed, information would be lost, as multiplication by 2 is equivalent to shifting. The advantage of using a prime is less clear, but it is traditional. A nice property of 31 is that the multiplication can be replaced by a shift and a subtraction for better performance: 31 * i == (i << 5) - i. Modern VMs do this sort of optimization automatically.
若是你前面沒看懂前面那段java代碼,如今應該明白(h << 5) - h的結果其實就等於31*h。 今天到這兒,後續將繼續分享其餘的算法。謝謝你們支持!