又是一个夜黑风高的晚上,带上无线耳机听一曲。忽然很感慨一句话:生活就像心电图,一路顺风就证实你挂了。
就如同咱们干运维的,以为很简单的事情,有时候能干出无限可能
。仍是言归正传吧,这一次咱们来讲说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 = 31*h + 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。 今天到这儿,后续将继续分享其余的算法。谢谢你们支持!