前言

又是一个夜黑风高的晚上,带上无线耳机听一曲。突然很感慨一句话:生活就像心电图,一帆风顺就证明你挂了。 就如同我们干运维的,觉得很简单的事情,有时候能干出无限可能。还是言归正传吧,这一次我们来说说stringhash分区算法。

1.hash分区算法

2.stringhash分区算法

3.enum分区算法

4.numberrange分区算法

5.patternrange分区算法

6.date分区算法

7.jumpstringhash算法

StringHash分区算法的配置

<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。

  • tableRule标签,name对应的是规则的名字,而rule标签中的columns则对应的分片字段,这个字段必须和表中的字段一致。algorithm则代表了执行分片函数的名字。
  • function标签,name代表分片算法的名字,算法的名字要和上面的tableRule中的标签相对应。class:指定分片算法实现类。property指定了对应分片算法的参数。不同的算法参数不同。

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开始索引计数

接下来我们来详细介绍一下StringHash的工作原理。我们以上面的配置为例。

1.在启动的时候,两个数组点乘做运算,得到取模数。

2.两个数组进行叉乘,得出物理分区表。

3.根据hashSlice二维数组,把分片字段的字符串进行截取。

字符串截取的范围是hashSlice[0]到hashSlice[1]。比如我这里设置0,3。‘buddy'这个字符串就会截取出bud,类似数据库中的substring函数。

4.将截取出来的字符串做hash,这个hash的计算方法我研究了一下dble的源代码。源代码如下:

 /**
* 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)。大家是不是还是云里雾里的。你可以去看文章结尾关于这一点的详细解释。

这里我们把这个公式分解一下,根据上述的公式,我们能推导出下列算术式:

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。正好和我们程序计算的值一样。

5.对计算出来的值取模,然后落在指定的分区中。

97905 mod 17 =2 根据取模的值,落在了dn1分区,dn1分区是存放(0,3)的。

6.让我们建表来测试一下,是不是落在第1个分区。



如图所示,当我们执行插入name='buddy',然后再一次查询的name='buddy'的时候,直接路由到了第一个分区。和我们之前计算的结果一致。

注意事项

  1. 该分区算法和hash分区算法有同样的限制(注意事项3除外)
  2. 分区字段为字符串类型

后记

今天介绍的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。

今天到这儿,后续将继续分享其他的算法。谢谢大家支持!

最新文章

  1. mint安装相关数据库lib
  2. 详解spring事务属性
  3. 10分钟学会理解和解决MySQL乱码问题
  4. DAY5 php + mysql 写一个简单的sql注入平台
  5. Timing path
  6. cocos2d-x创建精灵动画方式汇总
  7. Java 完美判断中文字符
  8. underscorejs-where学习
  9. jQuery提交form表单
  10. python多线程--theading模块
  11. 【1414软工助教】团队作业8——第二次项目冲刺(Beta阶段) 得分榜
  12. 02-Go语言数据类型与变量
  13. C++基础——类继承中方法重载
  14. Linux之文档与目录结构
  15. hdu 1811 Rank of Tetris - 拓扑排序 - 并查集
  16. .net如何处理高并发socket,建立高性能健壮的socket服务
  17. ios有些机型input和fixed导致的页面错位问题
  18. Hadoop生态圈-hbase介绍-完全分布式搭建
  19. 【Linux】- cat命令的源码历史
  20. Sql Server 日期格式化函數 Convert

热门文章

  1. JavaScript 开发者都应该知道的十个概念
  2. hdu 4128 Running relay (线性规划转半平面交)
  3. hdu 4114 Disney&#39;s FastPass(最短路+状态压缩)
  4. sublime 插件安装packagecontrol
  5. win10提示 磁盘包含不是“PARTITION_BASIC_DATA_GUID&quot;类型的分区
  6. AtCoder Beginner Contest 077 C Snuke Festival(二分)
  7. 有什么类方法或是函数可以查看某个项目的Laravel版本的?
  8. 2003年NOIP普及组复赛题解
  9. uni-app学习记录01-pages配置项
  10. node第一个参数必须是err